keepalived-2.3.3/0000775000175000017500000000000014772274317007411 5keepalived-2.3.3/autogen.sh0000775000175000017500000000014413767727575011345 #!/bin/sh mkdir -p build-aux aclocal --install -I m4 autoheader automake --add-missing autoreconf keepalived-2.3.3/configure0000775000175000017500000162315414772274257011257 #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for Keepalived 2.3.3. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, # Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="as_nop=: if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else \$as_nop case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ) then : else \$as_nop exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 blah=\$(echo \$(echo blah)) test x\"\$blah\" = xblah || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null then : as_have_required=yes else $as_nop as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null then : else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$as_shell as_have_required=yes if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null then : break 2 fi fi done;; esac as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop if { test -f "$SHELL" || test -f "$SHELL.exe"; } && as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null then : CONFIG_SHELL=$SHELL as_have_required=yes fi fi if test "x$CONFIG_SHELL" != x then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno then : printf "%s\n" "$0: This script requires a shell more modern than all" printf "%s\n" "$0: the shells that I found on your system." if test ${ZSH_VERSION+y} ; then printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." else printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and $0: keepalived-users@groups.io about your system, including $0: any error possibly output before this message. Then $0: install a modern shell, or manually run the script $0: under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_nop # --------- # Do nothing but, unlike ":", preserve the value of $?. as_fn_nop () { return $? } as_nop=as_fn_nop # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Keepalived' PACKAGE_TARNAME='keepalived' PACKAGE_VERSION='2.3.3' PACKAGE_STRING='Keepalived 2.3.3' PACKAGE_BUGREPORT='keepalived-users@groups.io' PACKAGE_URL='http://www.keepalived.org/' ac_unique_file="keepalived/core/main.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_header_c_list= ac_func_c_list= ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS INIT_SUSE_FALSE INIT_SUSE_TRUE INIT_OPENRC_FALSE INIT_OPENRC_TRUE INIT_SYSV_FALSE INIT_SYSV_TRUE INIT_SYSTEMD_FALSE INIT_SYSTEMD_TRUE INIT_UPSTART_FALSE INIT_UPSTART_TRUE REPRODUCIBLE_BUILD_FALSE REPRODUCIBLE_BUILD_TRUE KEEPALIVED_CONFIG_OPTIONS OLD_DEFAULT_CONFIG_FILE DEFAULT_CONFIG_FILENAME DEFAULT_CONFIG_DIR DEFAULT_CONFIG_FILE KEEPALIVED_RUNTIME_OPTIONS SYSTEMD_EXEC_START_OPTIONS WITH_SYSTEMD_NOTIFY_FALSE WITH_SYSTEMD_NOTIFY_TRUE SYSTEMD_SERVICE_TYPE systemdsystemunitdir RPM_BIP_FALSE RPM_BIP_TRUE RPM_FALSE RPM_TRUE HAVE_RPMBUILD HAVE_RPM WITH_SANITIZER_FALSE WITH_SANITIZER_TRUE PROFILE_FALSE PROFILE_TRUE ASSERTS_FALSE ASSERTS_TRUE ONE_PROCESS_DEBUG_FALSE ONE_PROCESS_DEBUG_TRUE BUILD_DOCS_FALSE BUILD_DOCS_TRUE HAVE_SPHINX_BUILD SPHINXBUILDNAME KA_TMP_DIR DBUS_CREATE_INSTANCE_FALSE DBUS_CREATE_INSTANCE_TRUE WITH_DBUS_FALSE WITH_DBUS_TRUE SNMP_SERVICE SNMP_REPLY_V3_FOR_V2_FALSE SNMP_REPLY_V3_FOR_V2_TRUE SNMP_RFCV3_FALSE SNMP_RFCV3_TRUE SNMP_RFCV2_FALSE SNMP_RFCV2_TRUE SNMP_RFC_FALSE SNMP_RFC_TRUE SNMP_CHECKER_FALSE SNMP_CHECKER_TRUE SNMP_VRRP_FALSE SNMP_VRRP_TRUE SNMP_KEEPALIVED_FALSE SNMP_KEEPALIVED_TRUE SNMP_FALSE SNMP_TRUE NETSNMP_CONFIG NETWORK_MANAGER_FALSE NETWORK_MANAGER_TRUE TRACK_PROCESS_FALSE TRACK_PROCESS_TRUE WITH_BFD_FALSE WITH_BFD_TRUE WITH_JSON_FALSE WITH_JSON_TRUE VMAC_FALSE VMAC_TRUE VRRP_AUTH_FALSE VRRP_AUTH_TRUE WITH_VRRP_FALSE WITH_VRRP_TRUE WITH_REGEX_FALSE WITH_REGEX_TRUE WITH_IPVS_FALSE WITH_IPVS_TRUE FIREWALL_FALSE FIREWALL_TRUE NFTABLES_FALSE NFTABLES_TRUE LIBIPSET_DYNAMIC_FALSE LIBIPSET_DYNAMIC_TRUE LIBIPTC_DYNAMIC_FALSE LIBIPTC_DYNAMIC_TRUE IPTABLES_FALSE IPTABLES_TRUE LIBIPSET_FALSE LIBIPSET_TRUE MAGIC_FALSE MAGIC_TRUE LIBNL_DYNAMIC_FALSE LIBNL_DYNAMIC_TRUE LIBNL3_FALSE LIBNL3_TRUE LIBNL1_FALSE LIBNL1_TRUE KA_LIBS KA_LDFLAGS KA_CFLAGS KA_CPPFLAGS WITH_STRICT_CONFIG_CHECKS_FALSE WITH_STRICT_CONFIG_CHECKS_TRUE DEBUG_FALSE DEBUG_TRUE RUNSTATEDIR DBUS_DATADIR EXPANDED_DATADIR ARFLAGS ac_ct_AR AR LDD SED LN_S GREP RANLIB am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC USE_LLD SAMPLES_DIR PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG MAINTAINERCLEANFILES AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V CSCOPE ETAGS CTAGS am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_lvs_syncd enable_lvs enable_lvs_64bit_stats enable_vrrp enable_bfd with_kernel_dir with_samples_dir enable_fwmark enable_snmp enable_snmp_vrrp enable_snmp_keepalived enable_snmp_checker enable_snmp_rfc enable_snmp_rfcv2 enable_snmp_rfcv3 enable_snmp_reply_v3_for_v2 enable_dbus enable_dbus_create_instance enable_regex enable_vmac enable_nm enable_regex_timers enable_json enable_clang enable_lto enable_reproducible_build with_init enable_vrrp_auth enable_checksum_compat enable_routes enable_linkbeat enable_sockaddr_storage enable_gnu_std_paths enable_dynamic_linking enable_iptables enable_libiptc_dynamic enable_libipset_dynamic enable_libnl_dynamic enable_libipset enable_nftables enable_libnl enable_track_process enable_systemd with_run_dir with_tmp_dir with_iproute_usr_dir with_iproute_etc_dir enable_strict_config_checks enable_hardening enable_optimise enable_warnings enable_extra_warnings enable_mem_check enable_mem_check_log enable_openssl_mem_check enable_malloc_check enable_timer_check enable_fault_flags_check enable_debug enable_netlink_timers enable_smtp_alert_debug enable_stacktrace enable_perf enable_sanitize_address enable_sanitize_address_options enable_sanitize_hwaddress enable_sanitize_hwaddress_options enable_sanitize_undefined enable_sanitize_undefined_options enable_sanitize_memory enable_sanitize_memory_options enable_sanitize_leak enable_sanitize_leak_options enable_sanitize_scudo enable_sanitize_scudo_options enable_log_file enable_dump_threads enable_epoll_debug enable_epoll_thread_dump enable_regex_debug enable_tsm_debug enable_vrrp_fd_debug enable_recvmsg_debug enable_eintr_debug enable_track_process_debug enable_parser_debug enable_checksum_debug enable_checker_debug enable_smtp_connect_debug enable_mem_err_debug enable_script_debug enable_one_process_debug enable_dump_keywords enable_network_timestamp enable_asserts with_fixed_if_type with_default_config_file with_default_runtime_options enable_profile enable_strict_cast_align enable_cast_align_checks enable_cast_via_void enable_conversion_checks enable_force_conversion_checks enable_Werror with_systemdsystemunitdir with_dbus_data_dir enable_cflags enable_cppflags enable_ldflags enable_dependency_tracking ' ac_precious_vars='build_alias host_alias target_alias PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR CC CFLAGS LDFLAGS LIBS CPPFLAGS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: \`$ac_useropt'" ac_useropt_orig=$ac_useropt ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures Keepalived 2.3.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/keepalived] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of Keepalived 2.3.3:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --disable-lvs-syncd do not use LVS synchronization daemon --disable-lvs do not use the LVS framework --disable-lvs-64bit-stats do not use the LVS 64-bit stats --disable-vrrp do not use the VRRP framework --enable-bfd use the BFD framework --disable-fwmark compile without SO_MARK support --enable-snmp compile with SNMP support --enable-snmp-vrrp compile with SNMP vrrp support --enable-snmp-keepalived obsolete - use --enable-snmp-vrrp --enable-snmp-checker compile with SNMP checker support --enable-snmp-rfc compile with SNMP RFC2787 (VRRPv2) and SNMP RFC6527 (VRRPv3) support --enable-snmp-rfcv2 compile with SNMP RFC2787 (VRRPv2) support --enable-snmp-rfcv3 compile with SNMP RFC6527 (VRRPv3) support --disable-snmp-reply-v3-for-v2 disable RFC6527 responses for VRRPv2 instances --enable-dbus compile with dbus support --enable-dbus-create-instance compile with dbus support for creating instances --enable-regex build with HTTP_GET regex checking --disable-vmac build without VMAC support --enable-nm enable setting VMACs unmanaged by NetworkManager (for pre 1.18 NM) --enable-regex-timers build with HTTP_GET regex timers --enable-json compile with signal to dump configuration and stats as json --enable-clang use clang compiler --enable-lto use Link Time Optimisation --enable-reproducible-build make builds reproducible --disable-vrrp-auth compile without VRRP authentication --disable-checksum-compat compile without v1.3.6 and earlier VRRPv3 unicast checksum compatibility --disable-routes compile without ip rules/routes --disable-linkbeat build without linkbeat support --enable-sockaddr-storage build using sockaddr_storage rather than smaller sockaddr for IPv4/6 only --enable-gnu-std-paths use GNU standard paths for pid files etc --enable-dynamic-linking compile with/without dynamically linked libiptc/libipset/libnl --disable-iptables compile without iptables support --enable-libiptc-dynamic compile with libiptc dynamically linked --disable-libipset-dynamic compile with libipset statically linked --enable-libnl-dynamic compile with libnl dynamically linked --disable-libipset compile without libipset --disable-nftables build without nftables support --disable-libnl compile without libnl --disable-track-process build without track-process functionality --disable-systemd build without systemd integration --enable-strict-config-checks build with strict configuration checking --disable-hardening do not build with security hardening --enable-optimise compiler optimisation level --enable-warnings[=WARNINGS] additional compiler warnings, disable for reduced set --enable-extra-warnings extra compiler warnings that will probably produce many warnings --enable-mem-check compile with memory alloc checking - e.g. no writes before or after buffer, everything allocated is freed --enable-mem-check-log compile with memory alloc checking writing to syslog --enable-openssl-mem-check compile with OpenSSL memory alloc checking - e.g. no writes before or after buffer, everything allocated is freed --enable-malloc-check compile with malloc checking - i.e. malloc etc returning NULL --enable-timer-check compile with set time logging --enable-fault-flags-check compile with checking VRRP fault flags --enable-debug compile with most debugging options --enable-netlink-timers compile with netlink command timers --enable-smtp-alert-debug compile with smtp-alert debugging --enable-stacktrace compile with stacktrace support --enable-perf compile with perf performance data recording support for vrrp process --enable-sanitize-address compile with sanitize=address (ASAN) support --enable-sanitize-address-options compile with sanitize=address (ASAN) default options --enable-sanitize-hwaddress compile with sanitize=hwaddress (HWASAN) support --enable-sanitize-hwaddress-options compile with sanitize=hwaddress (HWASAN) default options --enable-sanitize-undefined compile with sanitize=undefined (UBSAN) support --enable-sanitize-undefined-options compile with sanitize=undefined (UBSAN) default options --enable-sanitize-memory compile with sanitize=memory (MSAN) support --enable-sanitize-memory-options compile with sanitize=memory (MSAN) default options --enable-sanitize-leak compile with sanitize=leak (LSAN) support --enable-sanitize-leak-options compile with sanitize=leak (LSAN) default options --enable-sanitize-scudo compile with sanitize=scudo support --enable-sanitize-scudo-options compile with sanitize=scudo default options --enable-log-file enable logging to file (-g) --enable-dump-threads compile with thread dumping support --enable-epoll-debug compile with epoll_wait() debugging support --enable-epoll-thread-dump compile with epoll thread dumping support --enable-regex-debug compile with regex debugging support --enable-tsm-debug compile with TSM debugging support --enable-vrrp-fd-debug compile with vrrp fd debugging support --enable-recvmsg-debug compile with recvmsg() debugging support --enable-eintr-debug compile with EINTR debugging support, set to check/not check for EINTR --enable-track-process-debug compile with track process debugging support, set to log all process connector events --enable-parser-debug compile with parser debugging support --enable-checksum-debug compile with checksum debugging support --enable-checker-debug compile with checker debugging support --enable-smtp-connect-debug compile with smtp connect debugging support --enable-mem-err-debug compile with MALLOC/FREE error debugging support --enable-script-debug compile with script termination debugging support --enable-one-process-debug compile with all functionality running in a single process --enable-dump-keywords compile with keyword dumping support --enable-network-timestamp compile with network timestamp debugging support --enable-asserts compile with assert() enabled --enable-profile compile with profiling flags --enable-strict-cast-align compile with strict cast alignment warnings --enable-cast-align-checks runtime cast alignment checks --enable-cast-via-void enable pointer cast via void * (default if warns without) --disable-cast-via-void disable pointer cast via void * (default if does not warn without) --enable-conversion-checks compile with conversion warnings if sensible --enable-force-conversion-checks compile with conversion warnings --enable-Werror compile with warnings being errors --enable-cflags=flags additional CFLAGS --enable-cppflags=flags additional CPPFLAGS --enable-ldflags=flags additional LDFLAGS --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-kernel-dir=DIR path to linux kernel source directory --with-samples-dir=DIR specify location to install sample config files [SYSCONFDIR/samples] --with-init=(upstart|systemd|SYSV|SUSE|openrc) specify init type --with-run-dir=PATH_TO_RUN DEPRECATED - use --runstatedir=PATH_TO_RUN --with-tmp-dir=PATH_TO_TMP specify directory where run-time temporary files are to be located --with-iproute-usr-dir=PATH_TO_CONFIG_FILES specify usr directory iproute2 uses for config files --with-iproute-etc-dir=PATH_TO_CONFIG_FILES specify etc directory iproute2 uses for config files --with-fixed-if-type=TYPE treat interface type TYPE as unchangeable --with-default-config-file=FILE Default configuration file --with-default-runtime-options=OPTIONS Default runtime options --with-systemdsystemunitdir=DIR Directory for systemd service files --with-dbus-data-dir=DIR Directory for Dbus interface files Some influential environment variables: PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . Keepalived home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for configure.gnu first; this name is used for a wrapper for # Metaconfig's "Configure" on case-insensitive file systems. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF Keepalived configure 2.3.3 generated by GNU Autoconf 2.71 Copyright (C) 2021 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_run LINENO # ---------------------- # Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that # executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; } then : ac_retval=0 else $as_nop printf "%s\n" "$as_me: program exited with status $ac_status" >&5 printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_find_intX_t LINENO BITS VAR # ----------------------------------- # Finds a signed integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_intX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 printf %s "checking for int$2_t... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in int$2_t 'int' 'long int' \ 'long long int' 'short int' 'signed char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int main (void) { static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int main (void) { static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop case $ac_type in #( int$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if eval test \"x\$"$3"\" = x"no" then : else $as_nop break fi done fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_intX_t # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_uintX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 printf %s "checking for uint$2_t... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; test_array [0] = 0; return test_array [0]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : case $ac_type in #( uint$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if eval test \"x\$"$3"\" = x"no" then : else $as_nop break fi done fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_uintX_t # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 printf %s "checking for $2... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. */ #include #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main (void) { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR # ------------------------------------------------------------------ # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. ac_fn_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 printf %s "checking whether $as_decl_name is declared... " >&6; } if eval test \${$3+y} then : printf %s "(cached) " >&6 else $as_nop as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` eval ac_save_FLAGS=\$$6 as_fn_append $6 " $5" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main (void) { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$3=yes" else $as_nop eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext eval $6=\$ac_save_FLAGS fi eval ac_res=\$$3 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_check_decl # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 printf %s "checking for $2.$3... " >&6; } if eval test \${$4+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main (void) { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : eval "$4=yes" else $as_nop eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi eval ac_res=\$$4 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member ac_configure_args_raw= for ac_arg do case $ac_arg in *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append ac_configure_args_raw " '$ac_arg'" done case $ac_configure_args_raw in *$as_nl*) ac_safe_unquote= ;; *) ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. ac_unsafe_a="$ac_unsafe_z#~" ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; esac cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by Keepalived $as_me 2.3.3, which was generated by GNU Autoconf 2.71. Invocation command line was $ $0$ac_configure_args_raw _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac printf "%s\n" "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Sanitize IFS. IFS=" "" $as_nl" # Save into config.log some information that might help in debugging. { echo printf "%s\n" "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo printf "%s\n" "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then printf "%s\n" "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac printf "%s\n" "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then printf "%s\n" "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && printf "%s\n" "$as_me: caught signal $ac_signal" printf "%s\n" "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h printf "%s\n" "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. if test -n "$CONFIG_SITE"; then ac_site_files="$CONFIG_SITE" elif test "x$prefix" != xNONE; then ac_site_files="$prefix/share/config.site $prefix/etc/config.site" else ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi for ac_site_file in $ac_site_files do case $ac_site_file in #( */*) : ;; #( *) : ac_site_file=./$ac_site_file ;; esac if test -f "$ac_site_file" && test -r "$ac_site_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 printf "%s\n" "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 printf "%s\n" "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Test code for whether the C compiler supports C89 (global declarations) ac_c_conftest_c89_globals=' /* Does the compiler advertise C89 conformance? Do not test the value of __STDC__, because some compilers set it to 0 while being otherwise adequately conformant. */ #if !defined __STDC__ # error "Compiler does not advertise C89 conformance" #endif #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ struct buf { int x; }; struct buf * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not \xHH hex character constants. These do not provoke an error unfortunately, instead are silently treated as an "x". The following induces an error, until -std is added to get proper ANSI mode. Curiously \x00 != x always comes out true, for an array size at least. It is necessary to write \x00 == 0 to get something that is true only with -std. */ int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) '\''x'\'' int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), int, int);' # Test code for whether the C compiler supports C89 (body of main). ac_c_conftest_c89_main=' ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); ' # Test code for whether the C compiler supports C99 (global declarations) ac_c_conftest_c99_globals=' // Does the compiler advertise C99 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L # error "Compiler does not advertise C99 conformance" #endif #include extern int puts (const char *); extern int printf (const char *, ...); extern int dprintf (int, const char *, ...); extern void *malloc (size_t); // Check varargs macros. These examples are taken from C99 6.10.3.5. // dprintf is used instead of fprintf to avoid needing to declare // FILE and stderr. #define debug(...) dprintf (2, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK #error "your preprocessor is broken" #endif #if BIG_OK #else #error "your preprocessor is broken" #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) continue; return 0; } // Check varargs and va_copy. static bool test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str = ""; int number = 0; float fnumber = 0; while (*format) { switch (*format++) { case '\''s'\'': // string str = va_arg (args_copy, const char *); break; case '\''d'\'': // int number = va_arg (args_copy, int); break; case '\''f'\'': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); return *str && number && fnumber; } ' # Test code for whether the C compiler supports C99 (body of main). ac_c_conftest_c99_main=' // Check bool. _Bool success = false; success |= (argc != 0); // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[0] = argv[0][0]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' || dynamic_array[ni.number - 1] != 543); ' # Test code for whether the C compiler supports C11 (global declarations) ac_c_conftest_c11_globals=' // Does the compiler advertise C11 conformance? #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L # error "Compiler does not advertise C11 conformance" #endif // Check _Alignas. char _Alignas (double) aligned_as_double; char _Alignas (0) no_special_alignment; extern char aligned_as_int; char _Alignas (0) _Alignas (int) aligned_as_int; // Check _Alignof. enum { int_alignment = _Alignof (int), int_array_alignment = _Alignof (int[100]), char_alignment = _Alignof (char) }; _Static_assert (0 < -_Alignof (int), "_Alignof is signed"); // Check _Noreturn. int _Noreturn does_not_return (void) { for (;;) continue; } // Check _Static_assert. struct test_static_assert { int x; _Static_assert (sizeof (int) <= sizeof (long int), "_Static_assert does not work in struct"); long int y; }; // Check UTF-8 literals. #define u8 syntax error! char const utf8_literal[] = u8"happens to be ASCII" "another string"; // Check duplicate typedefs. typedef long *long_ptr; typedef long int *long_ptr; typedef long_ptr long_ptr; // Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. struct anonymous { union { struct { int i; int j; }; struct { int k; long int l; } w; }; int m; } v1; ' # Test code for whether the C compiler supports C11 (body of main). ac_c_conftest_c11_main=' _Static_assert ((offsetof (struct anonymous, i) == offsetof (struct anonymous, w.k)), "Anonymous union alignment botch"); v1.i = 2; v1.w.k = 5; ok |= v1.i != 5; ' # Test code for whether the C compiler supports C11 (complete). ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} ${ac_c_conftest_c11_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} ${ac_c_conftest_c11_main} return ok; } " # Test code for whether the C compiler supports C99 (complete). ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} ${ac_c_conftest_c99_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} ${ac_c_conftest_c99_main} return ok; } " # Test code for whether the C compiler supports C89 (complete). ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} int main (int argc, char **argv) { int ok = 0; ${ac_c_conftest_c89_main} return ok; } " as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" as_fn_append ac_header_c_list " vfork.h vfork_h HAVE_VFORK_H" as_fn_append ac_func_c_list " fork HAVE_FORK" as_fn_append ac_func_c_list " vfork HAVE_VFORK" # Auxiliary files required by this configure script. ac_aux_files="ar-lib compile missing install-sh" # Locations in which to look for auxiliary files. ac_aux_dir_candidates="${srcdir}/build-aux" # Search for a directory containing all of the required auxiliary files, # $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. # If we don't find one directory that contains all the files we need, # we report the set of missing files from the *first* directory in # $ac_aux_dir_candidates and give up. ac_missing_aux_files="" ac_first_candidate=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in $ac_aux_dir_candidates do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac as_found=: printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 ac_aux_dir_found=yes ac_install_sh= for ac_aux in $ac_aux_files do # As a special case, if "install-sh" is required, that requirement # can be satisfied by any of "install-sh", "install.sh", or "shtool", # and $ac_install_sh is set appropriately for whichever one is found. if test x"$ac_aux" = x"install-sh" then if test -f "${as_dir}install-sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 ac_install_sh="${as_dir}install-sh -c" elif test -f "${as_dir}install.sh"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 ac_install_sh="${as_dir}install.sh -c" elif test -f "${as_dir}shtool"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 ac_install_sh="${as_dir}shtool install -c" else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} install-sh" else break fi fi else if test -f "${as_dir}${ac_aux}"; then printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 else ac_aux_dir_found=no if $ac_first_candidate; then ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" else break fi fi fi done if test "$ac_aux_dir_found" = yes; then ac_aux_dir="$as_dir" break fi ac_first_candidate=false as_found=false done IFS=$as_save_IFS if $as_found then : else $as_nop as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. if test -f "${ac_aux_dir}config.guess"; then ac_config_guess="$SHELL ${ac_aux_dir}config.guess" fi if test -f "${ac_aux_dir}config.sub"; then ac_config_sub="$SHELL ${ac_aux_dir}config.sub" fi if test -f "$ac_aux_dir/configure"; then ac_configure="$SHELL ${ac_aux_dir}configure" fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu am__api_version='1.16' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 printf %s "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if test ${ac_cv_path_install+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac # Account for fact that we put trailing slashes in our PATH walk. case $as_dir in #(( ./ | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test ${ac_cv_path_install+y}; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 printf "%s\n" "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 printf %s "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`printf "%s\n" "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 printf "%s\n" "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5 printf %s "checking for a race-free mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if test ${ac_cv_path_mkdir+y} then : printf %s "(cached) " >&6 else $as_nop as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir ('*'coreutils) '* | \ 'BusyBox '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test ${ac_cv_path_mkdir+y}; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 printf "%s\n" "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AWK+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 printf "%s\n" "$AWK" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AWK" && break done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else $as_nop cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } SET_MAKE= else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test ${enable_silent_rules+y} then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 printf %s "checking whether $am_make supports nested variables... " >&6; } if test ${am_cv_make_support_nested_variables+y} then : printf %s "(cached) " >&6 else $as_nop if printf "%s\n" 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='keepalived' VERSION='2.3.3' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h printf "%s\n" "#define VERSION \"$VERSION\"" >>confdefs.h # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi if test -z "$ETAGS"; then ETAGS=etags fi if test -z "$CSCOPE"; then CSCOPE=cscope fi # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi ac_config_headers="$ac_config_headers lib/config.h lib/config_warnings.h" ac_config_files="$ac_config_files Makefile keepalived/Makefile lib/Makefile keepalived/core/Makefile keepalived.spec Dockerfile keepalived/check/Makefile keepalived/vrrp/Makefile keepalived/bfd/Makefile doc/Makefile bin_install/Makefile keepalived/dbus/Makefile keepalived/etc/Makefile keepalived/etc/init/Makefile keepalived/etc/init.d/Makefile keepalived/etc/sysconfig/Makefile keepalived/etc/keepalived/Makefile keepalived/trackers/Makefile doc/man/man8/Makefile doc/man/man5/Makefile doc/man/man1/Makefile" MAINTAINERCLEANFILES="*~ *.orig *.rej core core.*" CONFIG_OPTIONS= SYSTEM_OPTIONS= # Check whether --enable-silent-rules was given. if test ${enable_silent_rules+y} then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=0;; esac am_make=${MAKE-make} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 printf %s "checking whether $am_make supports nested variables... " >&6; } if test ${am_cv_make_support_nested_variables+y} then : printf %s "(cached) " >&6 else $as_nop if printf "%s\n" 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 printf "%s\n" "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 printf "%s\n" "$PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_PKG_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 printf "%s\n" "$ac_pt_PKG_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 printf %s "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } PKG_CONFIG="" fi fi # Check whether --enable-lvs-syncd was given. if test ${enable_lvs_syncd+y} then : enableval=$enable_lvs_syncd; fi # Check whether --enable-lvs was given. if test ${enable_lvs+y} then : enableval=$enable_lvs; fi # Check whether --enable-lvs-64bit-stats was given. if test ${enable_lvs_64bit_stats+y} then : enableval=$enable_lvs_64bit_stats; fi # Check whether --enable-vrrp was given. if test ${enable_vrrp+y} then : enableval=$enable_vrrp; fi # Check whether --enable-bfd was given. if test ${enable_bfd+y} then : enableval=$enable_bfd; fi # Check whether --with-kernel-dir was given. if test ${with_kernel_dir+y} then : withval=$with_kernel_dir; kernel_src_path="$withval" else $as_nop kernel_src_path="" fi # Check whether --with-samples-dir was given. if test ${with_samples_dir+y} then : withval=$with_samples_dir; fi # Check whether --enable-fwmark was given. if test ${enable_fwmark+y} then : enableval=$enable_fwmark; fi # Check whether --enable-snmp was given. if test ${enable_snmp+y} then : enableval=$enable_snmp; fi # Check whether --enable-snmp-vrrp was given. if test ${enable_snmp_vrrp+y} then : enableval=$enable_snmp_vrrp; fi # Check whether --enable-snmp-keepalived was given. if test ${enable_snmp_keepalived+y} then : enableval=$enable_snmp_keepalived; fi # Check whether --enable-snmp-checker was given. if test ${enable_snmp_checker+y} then : enableval=$enable_snmp_checker; fi # Check whether --enable-snmp-rfc was given. if test ${enable_snmp_rfc+y} then : enableval=$enable_snmp_rfc; fi # Check whether --enable-snmp-rfcv2 was given. if test ${enable_snmp_rfcv2+y} then : enableval=$enable_snmp_rfcv2; fi # Check whether --enable-snmp-rfcv3 was given. if test ${enable_snmp_rfcv3+y} then : enableval=$enable_snmp_rfcv3; fi # Check whether --enable-snmp-reply-v3-for-v2 was given. if test ${enable_snmp_reply_v3_for_v2+y} then : enableval=$enable_snmp_reply_v3_for_v2; fi # Check whether --enable-dbus was given. if test ${enable_dbus+y} then : enableval=$enable_dbus; fi # Check whether --enable-dbus-create-instance was given. if test ${enable_dbus_create_instance+y} then : enableval=$enable_dbus_create_instance; fi # Check whether --enable-regex was given. if test ${enable_regex+y} then : enableval=$enable_regex; fi # Check whether --enable-vmac was given. if test ${enable_vmac+y} then : enableval=$enable_vmac; fi # Check whether --enable-nm was given. if test ${enable_nm+y} then : enableval=$enable_nm; fi # Check whether --enable-regex-timers was given. if test ${enable_regex_timers+y} then : enableval=$enable_regex_timers; fi # Check whether --enable-json was given. if test ${enable_json+y} then : enableval=$enable_json; fi # Check whether --enable-clang was given. if test ${enable_clang+y} then : enableval=$enable_clang; fi # Check whether --enable-lto was given. if test ${enable_lto+y} then : enableval=$enable_lto; fi # Check whether --enable-reproducible-build was given. if test ${enable_reproducible_build+y} then : enableval=$enable_reproducible_build; fi # Check whether --with-init was given. if test ${with_init+y} then : withval=$with_init; init_type="$withval" else $as_nop init_type="" fi # Check whether --enable-vrrp-auth was given. if test ${enable_vrrp_auth+y} then : enableval=$enable_vrrp_auth; fi # Check whether --enable-checksum_compat was given. if test ${enable_checksum_compat+y} then : enableval=$enable_checksum_compat; fi # Check whether --enable-routes was given. if test ${enable_routes+y} then : enableval=$enable_routes; fi # Check whether --enable-linkbeat was given. if test ${enable_linkbeat+y} then : enableval=$enable_linkbeat; fi # Check whether --enable-sockaddr_storage was given. if test ${enable_sockaddr_storage+y} then : enableval=$enable_sockaddr_storage; fi # Check whether --enable-gnu-std-paths was given. if test ${enable_gnu_std_paths+y} then : enableval=$enable_gnu_std_paths; fi # Check whether --enable-dynamic-linking was given. if test ${enable_dynamic_linking+y} then : enableval=$enable_dynamic_linking; fi # Check whether --enable-iptables was given. if test ${enable_iptables+y} then : enableval=$enable_iptables; else $as_nop IPTABLES_SILENT=Yes fi # Check whether --enable-libiptc-dynamic was given. if test ${enable_libiptc_dynamic+y} then : enableval=$enable_libiptc_dynamic; fi # Check whether --enable-libipset-dynamic was given. if test ${enable_libipset_dynamic+y} then : enableval=$enable_libipset_dynamic; fi # Check whether --enable-libnl-dynamic was given. if test ${enable_libnl_dynamic+y} then : enableval=$enable_libnl_dynamic; fi # Check whether --enable-libipset was given. if test ${enable_libipset+y} then : enableval=$enable_libipset; fi # Check whether --enable-nftables was given. if test ${enable_nftables+y} then : enableval=$enable_nftables; else $as_nop NFTABLES_SILENT=Yes fi # Check whether --enable-libnl was given. if test ${enable_libnl+y} then : enableval=$enable_libnl; fi # Check whether --enable-track-process was given. if test ${enable_track_process+y} then : enableval=$enable_track_process; fi # Check whether --enable-systemd was given. if test ${enable_systemd+y} then : enableval=$enable_systemd; fi # Check whether --with-run-dir was given. if test ${with_run_dir+y} then : withval=$with_run_dir; fi # Check whether --with-tmp-dir was given. if test ${with_tmp_dir+y} then : withval=$with_tmp_dir; TMP_DIR_SPECIFIED=Y else $as_nop TMP_DIR_SPECIFIED=N fi # Check whether --with-iproute-usr-dir was given. if test ${with_iproute_usr_dir+y} then : withval=$with_iproute_usr_dir; fi # Check whether --with-iproute-etc-dir was given. if test ${with_iproute_etc_dir+y} then : withval=$with_iproute_etc_dir; fi # Check whether --enable-strict-config-checks was given. if test ${enable_strict_config_checks+y} then : enableval=$enable_strict_config_checks; fi # Check whether --enable-hardening was given. if test ${enable_hardening+y} then : enableval=$enable_hardening; fi # Check whether --enable-optimise was given. if test ${enable_optimise+y} then : enableval=$enable_optimise; else $as_nop enable_optimise=not-specified fi # Check whether --enable-warnings was given. if test ${enable_warnings+y} then : enableval=$enable_warnings; else $as_nop enable_warnings=yes fi # Check whether --enable-extra-warnings was given. if test ${enable_extra_warnings+y} then : enableval=$enable_extra_warnings; fi # Check whether --enable-mem-check was given. if test ${enable_mem_check+y} then : enableval=$enable_mem_check; fi # Check whether --enable-mem-check-log was given. if test ${enable_mem_check_log+y} then : enableval=$enable_mem_check_log; fi # Check whether --enable-openssl-mem-check was given. if test ${enable_openssl_mem_check+y} then : enableval=$enable_openssl_mem_check; fi # Check whether --enable-malloc-check was given. if test ${enable_malloc_check+y} then : enableval=$enable_malloc_check; fi # Check whether --enable-timer-check was given. if test ${enable_timer_check+y} then : enableval=$enable_timer_check; fi # Check whether --enable-fault-flags-check was given. if test ${enable_fault_flags_check+y} then : enableval=$enable_fault_flags_check; fi # Check whether --enable-debug was given. if test ${enable_debug+y} then : enableval=$enable_debug; fi # Check whether --enable-netlink-timers was given. if test ${enable_netlink_timers+y} then : enableval=$enable_netlink_timers; fi # Check whether --enable-smtp-alert-debug was given. if test ${enable_smtp_alert_debug+y} then : enableval=$enable_smtp_alert_debug; fi # Check whether --enable-stacktrace was given. if test ${enable_stacktrace+y} then : enableval=$enable_stacktrace; fi # Check whether --enable-perf was given. if test ${enable_perf+y} then : enableval=$enable_perf; fi # Check whether --enable-sanitize-address was given. if test ${enable_sanitize_address+y} then : enableval=$enable_sanitize_address; fi # Check whether --enable-sanitize-address-options was given. if test ${enable_sanitize_address_options+y} then : enableval=$enable_sanitize_address_options; fi # Check whether --enable-sanitize-hwaddress was given. if test ${enable_sanitize_hwaddress+y} then : enableval=$enable_sanitize_hwaddress; fi # Check whether --enable-sanitize-hwaddress-options was given. if test ${enable_sanitize_hwaddress_options+y} then : enableval=$enable_sanitize_hwaddress_options; fi # Check whether --enable-sanitize-undefined was given. if test ${enable_sanitize_undefined+y} then : enableval=$enable_sanitize_undefined; fi # Check whether --enable-sanitize-undefined-options was given. if test ${enable_sanitize_undefined_options+y} then : enableval=$enable_sanitize_undefined_options; fi # Check whether --enable-sanitize-memory was given. if test ${enable_sanitize_memory+y} then : enableval=$enable_sanitize_memory; fi # Check whether --enable-sanitize-memory-options was given. if test ${enable_sanitize_memory_options+y} then : enableval=$enable_sanitize_memory_options; fi # Check whether --enable-sanitize-leak was given. if test ${enable_sanitize_leak+y} then : enableval=$enable_sanitize_leak; fi # Check whether --enable-sanitize-leak-options was given. if test ${enable_sanitize_leak_options+y} then : enableval=$enable_sanitize_leak_options; fi # Check whether --enable-sanitize-scudo was given. if test ${enable_sanitize_scudo+y} then : enableval=$enable_sanitize_scudo; fi # Check whether --enable-sanitize-scudo-options was given. if test ${enable_sanitize_scudo_options+y} then : enableval=$enable_sanitize_scudo_options; fi # Check whether --enable-log-file was given. if test ${enable_log_file+y} then : enableval=$enable_log_file; fi # Check whether --enable-dump-threads was given. if test ${enable_dump_threads+y} then : enableval=$enable_dump_threads; fi # Check whether --enable-epoll-debug was given. if test ${enable_epoll_debug+y} then : enableval=$enable_epoll_debug; fi # Check whether --enable-epoll-thread-dump was given. if test ${enable_epoll_thread_dump+y} then : enableval=$enable_epoll_thread_dump; fi # Check whether --enable-regex-debug was given. if test ${enable_regex_debug+y} then : enableval=$enable_regex_debug; fi # Check whether --enable-tsm-debug was given. if test ${enable_tsm_debug+y} then : enableval=$enable_tsm_debug; fi # Check whether --enable-vrrp-fd-debug was given. if test ${enable_vrrp_fd_debug+y} then : enableval=$enable_vrrp_fd_debug; fi # Check whether --enable-recvmsg-debug was given. if test ${enable_recvmsg_debug+y} then : enableval=$enable_recvmsg_debug; fi # Check whether --enable-eintr-debug was given. if test ${enable_eintr_debug+y} then : enableval=$enable_eintr_debug; fi # Check whether --enable-track-process-debug was given. if test ${enable_track_process_debug+y} then : enableval=$enable_track_process_debug; fi # Check whether --enable-parser-debug was given. if test ${enable_parser_debug+y} then : enableval=$enable_parser_debug; fi # Check whether --enable-checksum-debug was given. if test ${enable_checksum_debug+y} then : enableval=$enable_checksum_debug; fi # Check whether --enable-checker-debug was given. if test ${enable_checker_debug+y} then : enableval=$enable_checker_debug; fi # Check whether --enable-smtp-connect-debug was given. if test ${enable_smtp_connect_debug+y} then : enableval=$enable_smtp_connect_debug; fi # Check whether --enable-mem-err-debug was given. if test ${enable_mem_err_debug+y} then : enableval=$enable_mem_err_debug; fi # Check whether --enable-script-debug was given. if test ${enable_script_debug+y} then : enableval=$enable_script_debug; fi # Check whether --enable-one-process-debug was given. if test ${enable_one_process_debug+y} then : enableval=$enable_one_process_debug; fi # Check whether --enable-dump-keywords was given. if test ${enable_dump_keywords+y} then : enableval=$enable_dump_keywords; fi # Check whether --enable-network-timestamp was given. if test ${enable_network_timestamp+y} then : enableval=$enable_network_timestamp; fi # Check whether --enable-asserts was given. if test ${enable_asserts+y} then : enableval=$enable_asserts; fi # Check whether --with-fixed-if-type was given. if test ${with_fixed_if_type+y} then : withval=$with_fixed_if_type; fi # Check whether --with-default-config-file was given. if test ${with_default_config_file+y} then : withval=$with_default_config_file; default_config_file="$withval" else $as_nop default_config_file="" fi # Check whether --with-default-runtime-options was given. if test ${with_default_runtime_options+y} then : withval=$with_default_runtime_options; default_runtime_options="$withval" else $as_nop default_runtime_options="-D" fi # Check whether --enable-profile was given. if test ${enable_profile+y} then : enableval=$enable_profile; fi # Check whether --enable-strict-cast-align was given. if test ${enable_strict_cast_align+y} then : enableval=$enable_strict_cast_align; fi # Check whether --enable-cast-align-checks was given. if test ${enable_cast_align_checks+y} then : enableval=$enable_cast_align_checks; fi # Check whether --enable-cast-via-void was given. if test ${enable_cast_via_void+y} then : enableval=$enable_cast_via_void; fi # Check whether --enable-conversion-checks was given. if test ${enable_conversion_checks+y} then : enableval=$enable_conversion_checks; fi # Check whether --enable-force-conversion-checks was given. if test ${enable_force_conversion_checks+y} then : enableval=$enable_force_conversion_checks; fi # Check whether --enable-Werror was given. if test ${enable_Werror+y} then : enableval=$enable_Werror; fi # Check whether --with-systemdsystemunitdir was given. if test ${with_systemdsystemunitdir+y} then : withval=$with_systemdsystemunitdir; else $as_nop with_systemdsystemunitdir=auto fi # Check whether --with-dbus-data-dir was given. if test ${with_dbus_data_dir+y} then : withval=$with_dbus_data_dir; fi # Check whether --enable-cflags was given. if test ${enable_cflags+y} then : enableval=$enable_cflags; fi # Check whether --enable-cppflags was given. if test ${enable_cppflags+y} then : enableval=$enable_cppflags; fi # Check whether --enable-ldflags was given. if test ${enable_ldflags+y} then : enableval=$enable_ldflags; fi # Set the kernel headers path if test -n "$kernel_src_path"; then if test ! -d $kernel_src_path/include; then as_fn_error $? "kernel source path $kernel_src_path/include does not exist" "$LINENO" 5 fi if test ! -d $kernel_src_path/include/linux; then as_fn_error $? "kernel source path $kernel_src_path/include does not appear to include linux header files" "$LINENO" 5 fi if test -d $kernel_src_path/include/uapi/linux; then as_fn_error $? "kernel source path $kernel_src_path appears to be an unprocessed kernel source tree" "$LINENO" 5 fi kernelinc="-isystem $kernel_src_path/include" elif test ! -d /usr/include/linux -a \ -d /usr/src/linux/include; then kernelinc="-isystem /usr/src/linux/include" else kernelinc= fi SRC_DIR=`$as_dirname -- $0 || $as_expr X$0 : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X$0 : 'X\(//\)[^/]' \| \ X$0 : 'X\(//\)$' \| \ X$0 : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X$0 | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` if test -n "$with_samples_dir" then : if test $with_samples_dir = yes -o $with_samples_dir = no then : as_fn_error $? "--with-samples-dir requires a directory" "$LINENO" 5 fi SAMPLES_DIR=$with_samples_dir else $as_nop SAMPLES_DIR="\${sysconfdir}/$PACKAGE/samples" fi CPPFLAGS="$kernelinc $CPPFLAGS" if test .$enable_clang = .yes then : USE_CC=clang if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lld", so it can be a program name with args. set dummy ${ac_tool_prefix}lld; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_USE_LLD+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$USE_LLD"; then ac_cv_prog_USE_LLD="$USE_LLD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_USE_LLD="${ac_tool_prefix}lld" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi USE_LLD=$ac_cv_prog_USE_LLD if test -n "$USE_LLD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $USE_LLD" >&5 printf "%s\n" "$USE_LLD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_USE_LLD"; then ac_ct_USE_LLD=$USE_LLD # Extract the first word of "lld", so it can be a program name with args. set dummy lld; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_USE_LLD+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_USE_LLD"; then ac_cv_prog_ac_ct_USE_LLD="$ac_ct_USE_LLD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_USE_LLD="lld" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_USE_LLD=$ac_cv_prog_ac_ct_USE_LLD if test -n "$ac_ct_USE_LLD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_USE_LLD" >&5 printf "%s\n" "$ac_ct_USE_LLD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_USE_LLD" = x; then USE_LLD=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac USE_LLD=$ac_ct_USE_LLD fi else USE_LLD="$ac_cv_prog_USE_LLD" fi if test $USE_LLD != : then : LDFLAGS+=" -fuse-ld=lld -rtlib=compiler-rt" fi else $as_nop USE_CC="gcc cc" fi # Checks for programs. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then for ac_prog in $USE_CC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 printf "%s\n" "$CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in $USE_CC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_CC+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 printf "%s\n" "$ac_ct_CC" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion -version; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 printf %s "checking whether the C compiler works... " >&6; } ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else $as_nop ac_file='' fi if test -z "$ac_file" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 printf %s "checking for C compiler default output file name... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 printf "%s\n" "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 printf %s "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else $as_nop { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 printf "%s\n" "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 printf %s "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 printf "%s\n" "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 printf %s "checking for suffix of object files... " >&6; } if test ${ac_cv_objext+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" printf "%s\n" "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_nop printf "%s\n" "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 printf "%s\n" "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 printf %s "checking whether the compiler supports GNU C... " >&6; } if test ${ac_cv_c_compiler_gnu+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_compiler_gnu=yes else $as_nop ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } ac_compiler_gnu=$ac_cv_c_compiler_gnu if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+y} ac_save_CFLAGS=$CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 printf %s "checking whether $CC accepts -g... " >&6; } if test ${ac_cv_prog_cc_g+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes else $as_nop CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 printf "%s\n" "$ac_cv_prog_cc_g" >&6; } if test $ac_test_CFLAGS; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi ac_prog_cc_stdc=no if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 printf %s "checking for $CC option to enable C11 features... " >&6; } if test ${ac_cv_prog_cc_c11+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c11=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c11_program _ACEOF for ac_arg in '' -std=gnu11 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c11=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c11" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c11" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c11" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } CC="$CC $ac_cv_prog_cc_c11" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 ac_prog_cc_stdc=c11 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 printf %s "checking for $CC option to enable C99 features... " >&6; } if test ${ac_cv_prog_cc_c99+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c99_program _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c99" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c99" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } CC="$CC $ac_cv_prog_cc_c99" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 ac_prog_cc_stdc=c99 fi fi if test x$ac_prog_cc_stdc = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 printf %s "checking for $CC option to enable C89 features... " >&6; } if test ${ac_cv_prog_cc_c89+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_c_conftest_c89_program _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO" then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext conftest.beam test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi if test "x$ac_cv_prog_cc_c89" = xno then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 printf "%s\n" "unsupported" >&6; } else $as_nop if test "x$ac_cv_prog_cc_c89" = x then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 printf "%s\n" "none needed" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } CC="$CC $ac_cv_prog_cc_c89" fi ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 ac_prog_cc_stdc=c89 fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 printf %s "checking whether $CC understands -c and -o together... " >&6; } if test ${am_cv_prog_cc_c_o+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 printf "%s\n" "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 printf %s "checking whether ${MAKE-make} supports the include directive... " >&6; } cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } case $?:`cat confinc.out 2>/dev/null` in #( '0:this is the am__doit target') : case $s in #( BSD) : am__include='.include' am__quote='"' ;; #( *) : am__include='include' am__quote='' ;; esac ;; #( *) : ;; esac if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 printf "%s\n" "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test ${enable_dependency_tracking+y} then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi depcc="$CC" am_compiler_list= { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 printf %s "checking dependency style of $depcc... " >&6; } if test ${am_cv_CC_dependencies_compiler_type+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 printf "%s\n" "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 printf %s "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`printf "%s\n" "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval test \${ac_cv_prog_make_${ac_make}_set+y} then : printf %s "(cached) " >&6 else $as_nop cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } SET_MAKE= else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 printf "%s\n" "$RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_RANLIB+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 printf "%s\n" "$ac_ct_RANLIB" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 printf %s "checking for grep that handles long lines and -e... " >&6; } if test ${ac_cv_path_GREP+y} then : printf %s "(cached) " >&6 else $as_nop if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in grep ggrep do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 printf "%s\n" "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 printf %s "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 printf "%s\n" "no, using $LN_S" >&6; } fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 printf %s "checking for a sed that does not truncate output... " >&6; } if test ${ac_cv_path_SED+y} then : printf %s "(cached) " >&6 else $as_nop ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_prog in sed gsed do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 printf %s 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" printf "%s\n" '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 printf "%s\n" "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 printf "%s\n" "$STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_STRIP+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 printf "%s\n" "$ac_ct_STRIP" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ldd", so it can be a program name with args. set dummy ${ac_tool_prefix}ldd; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_LDD+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$LDD"; then ac_cv_prog_LDD="$LDD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_LDD="${ac_tool_prefix}ldd" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LDD=$ac_cv_prog_LDD if test -n "$LDD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDD" >&5 printf "%s\n" "$LDD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_prog_LDD"; then ac_ct_LDD=$LDD # Extract the first word of "ldd", so it can be a program name with args. set dummy ldd; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_LDD+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_LDD"; then ac_cv_prog_ac_ct_LDD="$ac_ct_LDD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LDD="ldd" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LDD=$ac_cv_prog_ac_ct_LDD if test -n "$ac_ct_LDD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LDD" >&5 printf "%s\n" "$ac_ct_LDD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_ct_LDD" = x; then LDD="" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LDD=$ac_ct_LDD fi else LDD="$ac_cv_prog_LDD" fi if test -n "$ac_tool_prefix"; then for ac_prog in ar lib "link -lib" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_AR+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 printf "%s\n" "$AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar lib "link -lib" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_ac_ct_AR+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 printf "%s\n" "$ac_ct_AR" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 printf %s "checking the archiver ($AR) interface... " >&6; } if test ${am_cv_ar_interface+y} then : printf %s "(cached) " >&6 else $as_nop ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu am_cv_ar_interface=ar cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int some_variable = 0; _ACEOF if ac_fn_c_try_compile "$LINENO" then : am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 (eval $am_ar_try) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then am_cv_ar_interface=ar else am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 (eval $am_ar_try) 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then am_cv_ar_interface=lib else am_cv_ar_interface=unknown fi fi rm -f conftest.lib libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 printf "%s\n" "$am_cv_ar_interface" >&6; } case $am_cv_ar_interface in ar) ;; lib) # Microsoft lib, so override with the ar-lib wrapper script. # FIXME: It is wrong to rewrite AR. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__AR in this case, # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something # similar. AR="$am_aux_dir/ar-lib $AR" ;; unknown) as_fn_error $? "could not determine $AR interface" "$LINENO" 5 ;; esac ARFLAGS=cr # Default settings ENABLE_LOG_FILE_APPEND=No # AC_PROG_LIBTOOL # Ensure we don't override FORTIFY_SOURCE ADD_FORTIFY_SOURCE=1 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if _FORTIFY_SOURCE is enabled" >&5 printf %s "checking if _FORTIFY_SOURCE is enabled... " >&6; } SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -O2" SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS=$(echo $SAV_CPPFLAGS | sed -e "s/ ^ *-D *_FORTIFY_SOURCE=0-9*//") cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { printf("%d", _FORTIFY_SOURCE); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } ADD_FORTIFY_SOURCE=0 FORTIFY_SOURCE=$(echo | $CC -O3 -dM -E - | grep _FORTIFY_SOURCE | sed -e "s/.* //") else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CPPFLAGS=$SAV_CPPFLAGS CFLAGS=$SAV_CFLAGS if test $ADD_FORTIFY_SOURCE -eq 1 then : echo $CPPFLAGS | grep -q -- "-D *_FORTIFY_SOURCE=" if test $? -eq 0 then : FORTIFY_SOURCE=$(echo $CPPFLAGS | $SED -e "s/.*-D *_FORTIFY_SOURCE=//" -e "s/ .*//") ADD_FORTIFY_SOURCE=0 else $as_nop SAV_CFLAGS=$CFLAGS SAV_CPPFLAGS=$CPPFLAGS CFLAGS="$CFLAGS -Werror -O2" CPPFLAGS="$SAV_CPPFLAGS -D_FORTIFY_SOURCE=3" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) {} _ACEOF if ac_fn_c_try_compile "$LINENO" then : FORTIFY_SOURCE=3 else $as_nop CPPFLAGS="$SAV_CPPFLAGS -D_FORTIFY_SOURCE=2" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) {} _ACEOF if ac_fn_c_try_compile "$LINENO" then : FORTIFY_SOURCE=2 else $as_nop FORTIFY_SOURCE=1 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CPPFLAGS=$SAV_CPPFLAGS CFLAGS=$SAV_CFLAGS fi fi # # save the configure arguments # args=`echo $ac_configure_args | $SED -e "s/'//g"` if test .$enable_reproducible_build = .yes then : `echo $ac_configure_args | $SED -e "s/-ffile-prefix-map=[^ ]*//g"` fi printf "%s\n" "#define KEEPALIVED_CONFIGURE_OPTIONS \"$args\"" >>confdefs.h if test .$enable_lto = .yes then : if test .$enable_clang = .yes then : CFLAGS+=" -flto" else $as_nop CFLAGS+=" -flto=auto -ffat-lto-objects" fi fi # Save the CPPFLAGS, CFLAGS, LDFLAGS and LDLIBS settings for make time KA_CPPFLAGS="$kernelinc -D_GNU_SOURCE $CPPFLAGS" KA_CFLAGS="-g $CFLAGS" KA_LDFLAGS=$LDFLAGS KA_LIBS=$LDLIBS # Set any additional compiler flags if test -n "$enable_cflags" -a "$enable_cflags" != no then : KA_CFLAGS="$KA_CFLAGS "$enable_cflags"" fi if test -n "$enable_cppflags" -a "$enable_cppflags" != no then : KA_CPPFLAGS="$KA_CPPFLAGS "$enable_cppflags"" fi if test -n "$enable_ldflags" -a "$enable_ldflags" != no then : KA_LDFLAGS="$KA_LDFLAGS "$enable_ldflags"" fi # For reporting GCC bugs, uncomment the next two lines #KA_CPPFLAGS="$KA_CPPFLAGS -v -save-temps" #KA_CFLAGS="$KA_CFLAGS -v -save-temps" # To produce mixed source and assembly #KA_CFLAGS="$KA_CFLAGS -Wa,adhln" NEED_LIBDL=No #KA_LIBTOOLFLAGS = # Set up the compiler warnings we want MAX_FRAME_SIZE=5120 WARNINGS_BASIC="all extra unused strict-prototypes" WARNINGS_STD="abi absolute-value address-of-packed-member alloca alloc-larger-than=4096 alloc-zero arith-conversion array-bounds=2 attribute-alias=2 bad-function-cast c11-c2x-compat cast-align cast-qual chkp date-time disabled-optimization double-promotion duplicated-branches duplicated-cond float-conversion float-equal format-overflow format-security format-signedness format-truncation frame-larger-than=$MAX_FRAME_SIZE implicit-fallthrough=3 init-self inline invalid-pch jump-misses-init logical-op missing-declarations missing-field-initializers missing-include-dirs missing-prototypes nested-externs normalized null-dereference old-style-definition overlength-strings pointer-arith redundant-decls shadow shift-overflow=2 stack-protector strict-overflow=4 stringop-overflow=2 stringop-truncation suggest-attribute=cold suggest-attribute=const suggest-attribute=format suggest-attribute=malloc suggest-attribute=noreturn suggest-attribute=pure sync-nand trampolines undef uninitialized unknown-pragmas unsafe-loop-optimizations unsuffixed-float-constants unused-const-variable=2 unused-macros variadic-macros write-strings" WARNINGS_EXTRA="aggregate-return cast-align=strict conversion format-nonliteral format-overflow=2 format-truncation=2 padded pedantic sign-conversion stack-usage=$MAX_FRAME_SIZE strict-overflow=5 stringop-overflow=3 stringop-overflow=4 switch-enum system-headers traditional-conversion" # We want _GNU_SOURCE defined always CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" # fpclassify() needs -lm KA_LIBS="$KA_LIBS -lm" # Some sanity checks on configure options if test .$enable_vrrp = .no then : if test .$IPTABLES_SILENT == .Yes then : enable_iptables=no fi if test .$enable_perf != . then : as_fn_error $? "enable-perf requires vrrp" "$LINENO" 5 fi if test $with_fixed_if_type then : as_fn_error $? "with-fixed-if-type requires vrrp" "$LINENO" 5 fi if test .$enable_vrrp_fd_debug != . then : as_fn_error $? "enable-vrrp-fd-debug requires vrrp" "$LINENO" 5 fi if test .$enable_tsm_debug != . then : as_fn_error $? "enable-tsm-debug requires vrrp" "$LINENO" 5 fi if test .$enable_recvmsg_debug != . then : as_fn_error $? "enable-recvmsg-debug requires vrrp" "$LINENO" 5 fi if test .$enable_json != . then : as_fn_error $? "enable-json requires vrrp" "$LINENO" 5 fi if test .$enable_snmp_vrrp != . then : as_fn_error $? "enable-snmp-vrrp requires vrrp" "$LINENO" 5 fi if test .$enable_snmp_keepalived != . then : as_fn_error $? "enable-snmp-keepalived requires vrrp" "$LINENO" 5 fi if test .$enable_snmp_rfc != . then : as_fn_error $? "enable-snmp-rfc requires vrrp" "$LINENO" 5 fi if test .$enable_snmp_rfcv2 != . then : as_fn_error $? "enable-snmp-rfcv2 requires vrrp" "$LINENO" 5 fi if test .$enable_snmp_rfcv3 != . then : as_fn_error $? "enable-snmp-rfcv3 requires vrrp" "$LINENO" 5 fi if test .$enable_dbus != . then : as_fn_error $? "enable-dbus requires vrrp" "$LINENO" 5 fi if test .$enable_vrrp_auth != . then : as_fn_error $? "disable-vrrp-auth requires vrrp" "$LINENO" 5 fi if test .$enable_checksum_compat != . then : as_fn_error $? "disable-checksum-compat requires vrrp" "$LINENO" 5 fi if test .$enable_routes != . then : as_fn_error $? "disable-routes requires vrrp" "$LINENO" 5 fi if test .$enable_linkbeat != . then : as_fn_error $? "disable-linkbeat requires vrrp" "$LINENO" 5 fi if test .$enable_bfd != . then : as_fn_error $? "enable-bfd requires vrrp" "$LINENO" 5 fi if test .$enable_iptables != .no then : as_fn_error $? "disable-iptables requires vrrp" "$LINENO" 5 fi if test .$enable_track_process != . then : as_fn_error $? "disable-track-process requires vrrp" "$LINENO" 5 fi if test .$enable_network_timestamp != . then : as_fn_error $? "enable-network-timestamp requires vrrp" "$LINENO" 5 fi if test .$enable_netlink_timers != . then : as_fn_error $? "enable-netlink-timers requires vrrp" "$LINENO" 5 fi fi if test .$enable_iptables = .no then : if test .$enable_libipset != . then : as_fn_error $? "disable-libipset requires vrrp and iptables" "$LINENO" 5 fi fi if test .$enable_libipset = .no then : if test .$enable_libipset_dynamic != . then : as_fn_error $? "disable-libipset-dynamic requires ipsets" "$LINENO" 5 fi fi if test .$enable_snmp_rfc != .yes -a .$enable_snmp_rfcv3 != yes then : if test .$enable_snmp_reply_v3_for_v2 != . then : as_fn_error $? "disable-snmp-reply-v3-for-v2 requires enable-snmp-rfcv3 or enable-snmp-rfc" "$LINENO" 5 fi fi if test .$enable_dbus != .yes then : if test .$enable_dbus_create_instance != . then : as_fn_error $? "enable-dbus-create-instance requires enable-dbus" "$LINENO" 5 fi fi if test .$enable_lvs = .no then : if test .$enable_regex != . then : as_fn_error $? "enable-regex requires lvs" "$LINENO" 5 fi if test .$enable_libnl != . then : as_fn_error $? "disable-libnl requires lvs" "$LINENO" 5 fi if test .$enable_lvs_syncd != . then : as_fn_error $? "disable-lvs-syncd requires lvs" "$LINENO" 5 fi if test .$enable_lvs_64bit_stats != . then : as_fn_error $? "disable-lvs-64bit-stats requires lvs" "$LINENO" 5 fi if test .$enable_fwmark != . then : as_fn_error $? "disable-fwmark requires lvs" "$LINENO" 5 fi if test .$enable_checker_debug != . then : as_fn_error $? "enable-checker-debug requires lvs" "$LINENO" 5 fi if test .$enable_openssl_mem_check != . then : as_fn_error $? "enable-openssl-mem-check requires lvs" "$LINENO" 5 fi fi if test .$enable_libnl = .no then : if test .$enable_libnl_dynamic != . then : as_fn_error $? "enable-libnl-dynamic requires lvs and libnl" "$LINENO" 5 fi fi if test .$enable_regex != .yes then : if test .$enable_regex_timers != . then : as_fn_error $? "enable-regex-timers requires enable-regex" "$LINENO" 5 fi if test .$enable_regex_debug != . then : as_fn_error $? "enable-regex-debug requires enable-regex" "$LINENO" 5 fi fi if test .$enable_track_process = .no then : if test .$enable_track_process_debug != . then : as_fn_error $? "enable-track-process-debug incompatible with disable-track-process" "$LINENO" 5 fi fi if test .$enable_mem_check != .yes then : if test .$enable_mem_err_debug != . then : as_fn_error $? "enable-mem-err-debug requires --enable-mem-check" "$LINENO" 5 fi fi # --enable-debug enables most debugging if not explicitly disabled, but NOT one-process-debug if test .$enable_debug = .yes then : # If we are debugging, we want to be able to write logs to files if test .$enable_log_file = . then : enable_log_file=yes fi if test .$enable_asserts = . then : enable_asserts=yes fi if test .$enable_epoll_debug = . then : enable_epoll_debug=yes fi if test .$enable_epoll_thread_dump = . then : enable_epoll_thread_dump=yes fi if test .$enable_eintr_debug = . then : enable_eintr_debug=yes fi if test .$enable_parser_debug = . then : enable_parser_debug=yes fi if test .$enable_timer_check = . then : enable_timer_check=yes fi if test .$enable_smtp_alert_debug = . then : enable_smtp_alert_debug=yes fi if test .$enable_smtp_connect_debug = . then : enable_smtp_connect_debug=yes fi if test .$enable_dump_keywords = . then : enable_dump_keywords=yes fi if test .$enable_script_debug = . then : enable_script_debug=yes fi if test .$enable_vrrp != .no then : if test .$enable_vrrp_fd_debug = . then : enable_vrrp_fd_debug=yes fi if test .$enable_tsm_debug = . then : enable_tsm_debug=yes fi if test .$enable_recvmsg_debug = . then : enable_recvmsg_debug=yes fi if test .$enable_track_process_debug = . then : enable_track_process_debug=yes fi if test .$enable_checksum_debug = . then : enable_checksum_debug=yes fi if test .$enable_netlink_timers = . then : enable_netlink_timers=yes fi if test .$enable_network_timestamp = . then : enable_network_timestamp=yes fi if test .$enable_fault_flags_check = . then : enable_fault_flags_check=yes fi fi if test .$enable_lvs != .no then : if test .$enable_checker_debug = . then : enable_checker_debug=yes fi if test .$enable_regex = .yes then : if test .$enable_regex_timers = . then : enable_regex_timers=yes fi if test .$enable_regex_debug = . then : enable_regex_debug=yes fi fi fi if test .$enable_mem_check = .yes then : if test .$enable_mem_err_debug = . then : enable_mem_err_debug=yes fi fi fi if test .$with_run_dir != . then : as_fn_error $? "--with-run-dir is deprecated, use --runstatedir=${with_run_dir}/run instead" "$LINENO" 5 fi if test .$enable_strict_cast_align = .yes then : WARNINGS_STD=`echo $WARNINGS_STD | sed -e "s/cast-align /cast-align=strict /"` fi #### find the actual value for $datadir that we'll end up with ## (I know this is broken and should be done in the Makefile, but ## that's a major pain and almost nobody actually seems to care) ## In fact the "Built Sources Example" section of the automake ## manual suggests using configure as an option. EXP_VAR=EXPANDED_DATADIR FROM_VAR="$datadir" prefix_save=$prefix exec_prefix_save=$exec_prefix if test "x$prefix" = "xNONE"; then prefix="$ac_default_prefix" fi if test "x$exec_prefix" = "xNONE"; then exec_prefix=$prefix fi full_var="$FROM_VAR" while true; do new_full_var="`eval echo $full_var`" if test "x$new_full_var" = "x$full_var"; then break; fi full_var=$new_full_var done full_var=$new_full_var EXPANDED_DATADIR="$full_var" prefix=$prefix_save exec_prefix=$exec_prefix_save if test .$with_dbus_data_dir != . then : DBUS_DATADIR=$with_dbus_data_dir else $as_nop DBUS_DATADIR=$EXPANDED_DATADIR fi printf "%s\n" "#define DBUS_DATADIR \"$DBUS_DATADIR\"" >>confdefs.h echo "$ac_configure_args" | grep -q "'--r" RUNSTATEDIR_SPECIFIED=$? if test $RUNSTATEDIR_SPECIFIED -eq 1 then : echo "$ac_configure_args" | grep -q "'-runstatedir" RUNSTATEDIR_SPECIFIED=$? fi if test $RUNSTATEDIR_SPECIFIED -eq 0 then : EXP_VAR=RUNSTATEDIR FROM_VAR="$runstatedir" prefix_save=$prefix exec_prefix_save=$exec_prefix if test "x$prefix" = "xNONE"; then prefix="$ac_default_prefix" fi if test "x$exec_prefix" = "xNONE"; then exec_prefix=$prefix fi full_var="$FROM_VAR" while true; do new_full_var="`eval echo $full_var`" if test "x$new_full_var" = "x$full_var"; then break; fi full_var=$new_full_var done full_var=$new_full_var RUNSTATEDIR="$full_var" prefix=$prefix_save exec_prefix=$exec_prefix_save else $as_nop RUNSTATEDIR=/run fi printf "%s\n" "#define RUNSTATEDIR \"$RUNSTATEDIR\"" >>confdefs.h GCC_LTO=no if test $CC = gcc then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if using GCC Link Time Optimisation" >&5 printf %s "checking if using GCC Link Time Optimisation... " >&6; } echo " $CFLAGS " | grep -q -- " -flto=auto " if test $? -eq 0 then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } GCC_LTO=yes else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test $GCC_LTO = yes then : printf "%s\n" "#define GCC_LTO_NOINLINE __attribute__((noinline)) " >>confdefs.h else $as_nop printf "%s\n" "#define GCC_LTO_NOINLINE /**/" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking msghdr.msg_controllen is size_t" >&5 printf %s "checking msghdr.msg_controllen is size_t... " >&6; } SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(void) { struct msghdr msgh = { .msg_controllen = 0 }; printf("%zu", msgh.msg_controllen); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define PRI_MSG_CONTROLLEN \"zu\" " >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define PRI_MSG_CONTROLLEN \"u\" " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$SAV_CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking time print types" >&5 printf %s "checking time print types... " >&6; } SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -Wformat -Wformat-signedness" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { } _ACEOF if ac_fn_c_try_compile "$LINENO" then : signs="d u" WARN_SIGN="-Werror=format-signedness" else $as_nop signs="d" WARN_SIGN="" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$SAV_CFLAGS -Werror=format $WARN_SIGN" for field in t tv.tv_sec tv.tv_usec ts.tv_sec ts.tv_nsec; do for sign in $signs; do for len in "" l ll; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main(void) { time_t t = 1; struct timeval tv = { .tv_sec = 2 }; struct timespec ts = { .tv_sec = 3 }; printf("%$len$sign", $field); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if [ $field = t ]; then name=time_t else name=$(echo $field | $SED -e "s/\.tv//") fi printf "%s\n" "#define PRI_$name \"$len$sign\" " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done done done CFLAGS=$SAV_CFLAGS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5 printf "%s\n" "done" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking diagnostic pragmas in functions" >&5 printf %s "checking diagnostic pragmas in functions... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { _Pragma("GCC diagnostic warning \"-Wall\"") } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_FUNCTION_DIAGNOSTIC_PRAGMAS_ 1 " >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test .$enable_debug = .yes; then DEBUG_TRUE= DEBUG_FALSE='#' else DEBUG_TRUE='#' DEBUG_FALSE= fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking diagnostic push/pop pragmas" >&5 printf %s "checking diagnostic push/pop pragmas... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { _Pragma("GCC diagnostic push") } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_DIAGNOSTIC_PUSH_POP_PRAGMAS_ 1 " >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test ".$enable_warnings" = .no then : WARNINGS_ENABLED=$WARNINGS_BASIC else $as_nop WARNINGS_ENABLED="$WARNINGS_BASIC $WARNINGS_STD" if test ".$enable_warnings" != .yes then : WARN_LIST=`echo $enable_warnings | sed -e "s/-W//g"` WARNINGS_ENABLED="$WARNINGS_ENABLED "$WARN_LIST"" fi fi if test .$enable_extra_warnings = .yes then : WARNINGS_ENABLED="$WARNINGS_ENABLED "$WARNINGS_EXTRA"" fi if test "$enable_conversion_checks" = yes; then # Check if we can sensibly enable -Wconversion { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for usable -Wconversion" >&5 printf %s "checking for usable -Wconversion... " >&6; } SAV_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wconversion -O2 -Werror" if test $ADD_FORTIFY_SOURCE -eq 1 then : CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=$FORTIFY_SOURCE" fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #define VAL 255 static void fun(uint8_t val) { } int main(void) { fd_set set; uint8_t val = 42; unsigned u; bool b; size_t size = 17; char c[2]; char *c_ptr = c; struct rtattr rta; struct rtattr *rta_p = &rta; FD_SET(argc+1, &set); fun(argc == VAL ? VAL : val); // vrrp->lower_prio_no_advert = vrrp->strict_mode ? true : global_data->vrrp_lower_prio_no_advert; u = u ? true : b; size = RTA_LENGTH(size); c_ptr = RTA_DATA(c_ptr); rta_p = RTA_NEXT(rta_p, size); val = (u < 256 ) ? u & 0xff : 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } WARNINGS_ENABLED="$WARNINGS_ENABLED conversion" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: -Wconversion is not sensible with this compiler. Use --enable-force-conversion-checks to override." >&5 printf "%s\n" "$as_me: WARNING: -Wconversion is not sensible with this compiler. Use --enable-force-conversion-checks to override." >&2;} fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$SAV_CFLAGS" elif test "$enable_force_conversion_checks" = yes; then WARNINGS_ENABLED="$WARNINGS_ENABLED conversion" fi if test "$enable_Werror" = yes; then WARNINGS_ENABLED="$WARNINGS_ENABLED error" fi CONFIG_WARNINGS=$SRC_DIR/lib/config_warnings.h.in SAV_CFLAGS="$CFLAGS" for WARN in $WARNINGS_ENABLED do { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -W$WARN" >&5 printf %s "checking for -W$WARN... " >&6; } CFLAGS="$SAV_CFLAGS -W$WARN -Werror" WARN_VAR=_HAVE_WARNING_`echo $WARN | tr "a-z=-" "A-Z__"`_ LOCAL_WARN_VAR=HAVE_WARNING_`echo $WARN | sed -e "s/=.*//" | tr "a-z-" "A-Z_"` grep -q "^#undef $WARN_VAR$" $CONFIG_WARNINGS if test $? -ne 0 then : echo -e "\n/* Define to 1 if -W$WARN in use */\n#undef $WARN_VAR" >>$CONFIG_WARNINGS fi test `echo $WARN | grep "=[0-9][0-9]*$"` if test $? -eq 0 then : WARN_SHORT=`echo $WARN | sed -e 's/=[0-9][0-9]*$//'` WARN_VAR_SHORT=_HAVE_WARNING_`echo $WARN_SHORT | tr "a-z=-" "A-Z__"`_ grep -q "^#undef $WARN_VAR_SHORT$" $CONFIG_WARNINGS if test $? -ne 0 then : echo -e "\n/* Define to 1 if -W$WARN_SHORT in use */\n#undef $WARN_VAR_SHORT" >>$CONFIG_WARNINGS fi else $as_nop unset WARN_VAR_SHORT fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { } _ACEOF if ac_fn_c_try_compile "$LINENO" then : # gcc 9 removed -Wchkp and doesn't error if it is specified, # but rather outputs: # warning: switch '-Wchkp' is no longer supported # so check for the warning. touch conftest.err grep -q "is no longer supported" conftest.err if test $? -ne 0 then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } eval $LOCAL_WARN_VAR=yes KA_CFLAGS="$KA_CFLAGS -W$WARN" printf "%s\n" "#define $WARN_VAR 1 " >>confdefs.h if test -z "$WARN_VAR_SHORT" then : else $as_nop printf "%s\n" "#define $WARN_VAR_SHORT 1 " >>confdefs.h fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } eval $LOCAL_WARN_VAR=no fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } eval $LOCAL_WARN_VAR=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done if test .$HAVE_WARNING_STRICT_OVERFLOW = .yes then : # The following is not supported in gcc 5.4.0 CFLAGS="$CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ _Pragma("GCC diagnostic warning \"-Wstrict-overflow=1\"") int main(void) { } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define _HAVE_PRAGMA_WARN_STRICT_OVERFLOW_1_ 1 " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi CFLAGS="$SAV_CFLAGS" # keepalived is not suitable for assuming strict aliasing (it does a lot of type casting) KA_CFLAGS="$KA_CFLAGS -fno-strict-aliasing" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for function __attribute__((error(msg))) support" >&5 printf %s "checking for function __attribute__((error(msg))) support... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include static int func(int i) __attribute__((error("deliberate-error"))); static int func(int i) { return i * 2; } int main (void) { int i = func(2); printf("%d", i); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_FUNCTION_ATTRIBUTE_ERROR_ 1 " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext SAV_CFLAGS="$CFLAGS" CFLAGS="$SAV_CFLAGS -Werror=attributes" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for function __attribute__((warn_unused_result)) support" >&5 printf %s "checking for function __attribute__((warn_unused_result)) support... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include static int func(int i) __attribute__((warn_unused_result)); static int func(int i) { return i * 2; } int main (void) { func(2); printf("%d", 2); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_FUNCTION_WARN_UNUSED_RESULTS_ 1 " >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$SAV_CFLAGS" STRICT_CONFIG=No if test "$enable_strict_config_checks" = yes; then printf "%s\n" "#define _STRICT_CONFIG_ 1 " >>confdefs.h STRICT_CONFIG=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS STRICT_CONFIG" fi if test $STRICT_CONFIG = Yes; then WITH_STRICT_CONFIG_CHECKS_TRUE= WITH_STRICT_CONFIG_CHECKS_FALSE='#' else WITH_STRICT_CONFIG_CHECKS_TRUE='#' WITH_STRICT_CONFIG_CHECKS_FALSE= fi if test "$enable_hardening" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for PIE support" >&5 printf %s "checking for PIE support... " >&6; } SAV_CFLAGS="$CFLAGS" SAV_LDFLAGS="$LDFLAGS" CFLAGS="$CFLAGS -fPIE" if test "${enable_profile}" = yes; then # RHEL 7 and others have a problem with profiling with PIE CFLAGS="$CFLAGS -pg" fi LDFLAGS="$LDFLAGS -pie" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { int i = 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } KA_CFLAGS="$KA_CFLAGS -fPIE" KA_LDFLAGS="$KA_LDFLAGS -pie" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$SAV_CFLAGS LDFLAGS=$SAV_LDFLAGS for FLAG in \ "-Wformat -Werror=format-security" \ "-fexceptions" \ "-fstack-protector-strong" \ "--param=ssp-buffer-size=4" \ "-grecord-gcc-switches" do { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $FLAG support" >&5 printf %s "checking for $FLAG support... " >&6; } CFLAGS="$CFLAGS $FLAG" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } KA_CFLAGS="$KA_CFLAGS $FLAG" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$SAV_CFLAGS done if test $ADD_FORTIFY_SOURCE -eq 1 then : FLAG="-D_FORTIFY_SOURCE=$FORTIFY_SOURCE" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $FLAG support" >&5 printf %s "checking for $FLAG support... " >&6; } CFLAGS="$CFLAGS -O2 $FLAG" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } KA_CFLAGS="$KA_CFLAGS $FLAG" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$SAV_CFLAGS fi WL_FLAGS= for FLAG in \ "-z,relro" \ "-z,now" do { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -Wl,$FLAG support" >&5 printf %s "checking for -Wl,$FLAG support... " >&6; } LDFLAGS="$LDFLAGS -Wl,$FLAG" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { int i = 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } WL_FLAGS="$WL_FLAGS -Wl,$FLAG" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext CFLAGS=$SAV_CFLAGS LDFLAGS=$SAV_LDFLAGS done if test -n "$WL_FLAGS"; then KA_LDFLAGS="$KA_LDFLAGS $WL_FLAGS" fi fi # enable-optimise if test "$enable_optimise" = yes -o "$enable_optimise" = not-specified then : optimise_level=2 else $as_nop optimise_level=$enable_optimise fi if test "$enable_optimise" = no then : optimise_level=0 fi if test "$optimise_level" -eq 0 then : echo $KA_CFLAGS | $GREP -q -- "-D_FORTIFY_SOURCE=[^0]" if test $? -eq 0 then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: --disable-optimise requires --disable-hardening" >&5 printf "%s\n" "$as_me: WARNING: --disable-optimise requires --disable-hardening" >&2;} fi fi FLAG="-O$optimise_level" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $FLAG support" >&5 printf %s "checking for $FLAG support... " >&6; } SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $FLAG" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } KA_CFLAGS="$KA_CFLAGS $FLAG" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } if test "$enable_optimise" != not-specified then : as_fn_error $? "Invalid optimisation level specified" "$LINENO" 5 fi fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$SAV_CFLAGS # AC_SUBST(KA_LIBTOOLFLAGS) # Check for fallthrough attribute echo CFLAGS=$KA_CFLAGS SAV_CFLAGS=$CFLAGS CFLAGS="$KA_CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(int argc, char **argv) { switch(argc) { case 1: argv[0][0] = argv[0][1]; [[fallthrough]]; case 2: argv[1][0] = argv[1][1]; break; } } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define __fallthrough [[fallthrough]] " >>confdefs.h else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(int argc, char **argv) { switch(argc) { case 1: argv[0][0] = argv[0][1]; __attribute__((fallthrough)); case 2: argv[1][0] = argv[1][1]; break; } } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define __fallthrough __attribute__((fallthrough)) " >>confdefs.h else $as_nop printf "%s\n" "#define __fallthrough " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$SAV_CFLAGS # Check if unaligned memory access is supported (for ARM not supported prior to ARMv6 processors) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for unaligned memory access" >&5 printf %s "checking for unaligned memory access... " >&6; } if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unknown" >&5 printf "%s\n" "unknown" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Cannot determine if unaligned access supported. Assuming yes." >&5 printf "%s\n" "$as_me: WARNING: Cannot determine if unaligned access supported. Assuming yes." >&2;} else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if __BYTE_ORDER == __BIG_ENDIAN #if ULONG_MAX == 0xffffffffffffffffUL #define CHK_VAL 0x1234567890abcdefUL #elif ULONG_MAX == 0xffffffffUL #define CHK_VAL 0x12345678UL #else #define CHK_VAL 0x1234UL #endif #elif __BYTE_ORDER == __LITTLE_ENDIAN #if ULONG_MAX == 0xffffffffffffffffUL #define CHK_VAL 0xefcdab9078563412UL #elif ULONG_MAX == 0xffffffffUL #define CHK_VAL 0x78563412UL #else #define CHK_VAL 0x3412UL #endif #else #error Neither big nor little endian - unsupported #endif int main (void) { unsigned long arr[2] = { 0, 0 }; unsigned char *p = (unsigned char *)arr + 1; unsigned i; *(unsigned long *)p = CHK_VAL; return !!(arr[0] == CHK_VAL || p[0] != 0x12 || p[1] != 0x34 || p[2] != 0x56); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define _NO_UNALIGNED_ACCESS_ 1 " >>confdefs.h fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi # Check if unaligned memory access creates warnings { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for unaligned memory access causes warnings" >&5 printf %s "checking for unaligned memory access causes warnings... " >&6; } UNALIGNED_ACCESS_WARNED=0 SAV_CFLAGS="$CFLAGS" CFLAGS=`echo $KA_CFLAGS | sed -e "s/.*\(-Wcast-align[^ ]*\) .*/\1/"` CFLAGS="$CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { char c; int i = *(int *)&c; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } UNALIGNED_ACCESS_WARNED=1 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$SAV_CFLAGS" if test .$enable_cast_via_void = .no then : else $as_nop if test .$enable_cast_via_void = .yes -o $UNALIGNED_ACCESS_WARNED -eq 1 then : printf "%s\n" "#define CAST_VIA_VOID 1 " >>confdefs.h fi fi if test .$enable_cast_align_checks = .yes then : printf "%s\n" "#define CHECK_CAST_ALIGN 1 " >>confdefs.h fi # Checks for libraries. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for clock_gettime() requires -lrt" >&5 printf %s "checking for clock_gettime() requires -lrt... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { int i; struct timespec ts; i = clock_gettime(CLOCK_MONOTONIC, &ts); } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop SAV_LIBS="$LIBS" LIBS="$LIBS -lrt" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { int i; struct timespec ts; i = clock_gettime(CLOCK_MONOTONIC, &ts); } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } KA_LIBS="$KA_LIBS -lrt" else $as_nop as_fn_error $? "clock_gettime() not supported" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$SAV_LIBS fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # Checks for header files. ac_header= ac_cache= for ac_item in $ac_header_c_list do if test $ac_cache; then ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then printf "%s\n" "#define $ac_item 1" >> confdefs.h fi ac_header= ac_cache= elif test $ac_header; then ac_cache=$ac_item else ac_header=$ac_item fi done if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes then : printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h fi for ac_header in arpa/inet.h fcntl.h limits.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/param.h sys/prctl.h sys/socket.h sys/time.h syslog.h unistd.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop as_fn_error $? "Missing/unusable system header file <$ac_header>" "$LINENO" 5 fi done if test .$enable_lvs != .no then : for ac_header in net/if_arp.h do : ac_fn_c_check_header_compile "$LINENO" "net/if_arp.h" "ac_cv_header_net_if_arp_h" "$ac_includes_default" if test "x$ac_cv_header_net_if_arp_h" = xyes then : printf "%s\n" "#define HAVE_NET_IF_ARP_H 1" >>confdefs.h else $as_nop as_fn_error $? "Missing/unusable <$ac_header> - is glibc headers package installed?" "$LINENO" 5 fi done fi # check for kernel headers SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" for ac_header in linux/types.h do : ac_fn_c_check_header_compile "$LINENO" "linux/types.h" "ac_cv_header_linux_types_h" "$ac_includes_default" if test "x$ac_cv_header_linux_types_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_TYPES_H 1" >>confdefs.h else $as_nop as_fn_error $? "Missing/unusable kernel header file <$ac_header> - is kernel headers package installed?" "$LINENO" 5 fi done if test .$enable_vrrp != .no then : for ac_header in linux/ethtool.h linux/if_ether.h linux/if_packet.h linux/ip.h linux/sockios.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop as_fn_error $? "Missing/unusable kernel header file <$ac_header> - is kernel headers package installed?" "$LINENO" 5 fi done for ac_header in linux/fib_rules.h linux/if_addr.h linux/if_link.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$NETLINK_EXTRA_INCLUDE " if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop as_fn_error $? "Missing/unusable kernel header file <$ac_header> - is kernel headers package installed?" "$LINENO" 5 fi done fi if test .$enable_lvs != .no then : for ac_header in linux/icmp.h linux/icmpv6.h linux/errqueue.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop as_fn_error $? "Missing/unusable kernel header file <$ac_header> - is kernel headers package installed?" "$LINENO" 5 fi done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking linux/errqueue.h needs sys/time.h" >&5 printf %s "checking linux/errqueue.h needs sys/time.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(void) { } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define ERRQUEUE_NEEDS_SYS_TIME 1 " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi CPPFLAGS="$SAV_CPPFLAGS" # Checks for typedefs, structures, and compiler characteristics. ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" if test "x$ac_cv_type__Bool" = xyes then : printf "%s\n" "#define HAVE__BOOL 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 printf %s "checking for stdbool.h that conforms to C99... " >&6; } if test ${ac_cv_header_stdbool_h+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #ifndef __bool_true_false_are_defined #error "__bool_true_false_are_defined is not defined" #endif char a[__bool_true_false_are_defined == 1 ? 1 : -1]; /* Regardless of whether this is C++ or "_Bool" is a valid type name, "true" and "false" should be usable in #if expressions and integer constant expressions, and "bool" should be a valid type name. */ #if !true #error "'true' is not true" #endif #if true != 1 #error "'true' is not equal to 1" #endif char b[true == 1 ? 1 : -1]; char c[true]; #if false #error "'false' is not false" #endif #if false != 0 #error "'false' is not equal to 0" #endif char d[false == 0 ? 1 : -1]; enum { e = false, f = true, g = false * true, h = true * 256 }; char i[(bool) 0.5 == true ? 1 : -1]; char j[(bool) 0.0 == false ? 1 : -1]; char k[sizeof (bool) > 0 ? 1 : -1]; struct sb { bool s: 1; bool t; } s; char l[sizeof s.t > 0 ? 1 : -1]; /* The following fails for HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ bool m[h]; char n[sizeof m == h * sizeof m[0] ? 1 : -1]; char o[-1 - (bool) 0 < 0 ? 1 : -1]; /* Catch a bug in an HP-UX C compiler. See https://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html https://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html */ bool p = true; bool *pp = &p; /* C 1999 specifies that bool, true, and false are to be macros, but C++ 2011 and later overrule this. */ #if __cplusplus < 201103 #ifndef bool #error "bool is not defined" #endif #ifndef false #error "false is not defined" #endif #ifndef true #error "true is not defined" #endif #endif /* If _Bool is available, repeat with it all the tests above that used bool. */ #ifdef HAVE__BOOL struct sB { _Bool s: 1; _Bool t; } t; char q[(_Bool) 0.5 == true ? 1 : -1]; char r[(_Bool) 0.0 == false ? 1 : -1]; char u[sizeof (_Bool) > 0 ? 1 : -1]; char v[sizeof t.t > 0 ? 1 : -1]; _Bool w[h]; char x[sizeof m == h * sizeof m[0] ? 1 : -1]; char y[-1 - (_Bool) 0 < 0 ? 1 : -1]; _Bool z = true; _Bool *pz = &p; #endif int main (void) { bool ps = &s; *pp |= p; *pp |= ! p; #ifdef HAVE__BOOL _Bool pt = &t; *pz |= z; *pz |= ! z; #endif /* Refer to every declared value, so they cannot be discarded as unused. */ return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !j + !k + !l + !m + !n + !o + !p + !pp + !ps #ifdef HAVE__BOOL + !q + !r + !u + !v + !w + !x + !y + !z + !pt #endif ); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_header_stdbool_h=yes else $as_nop ac_cv_header_stdbool_h=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 printf "%s\n" "$ac_cv_header_stdbool_h" >&6; } if test $ac_cv_header_stdbool_h = yes; then printf "%s\n" "#define HAVE_STDBOOL_H 1" >>confdefs.h fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 printf %s "checking for inline... " >&6; } if test ${ac_cv_c_inline+y} then : printf %s "(cached) " >&6 else $as_nop ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo (void) {return 0; } $ac_kw foo_t foo (void) {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 printf "%s\n" "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" case $ac_cv_c_int64_t in #( no|yes) ;; #( *) printf "%s\n" "#define int64_t $ac_cv_c_int64_t" >>confdefs.h ;; esac ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default " if test "x$ac_cv_type_pid_t" = xyes then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined _WIN64 && !defined __CYGWIN__ LLP64 #endif int main (void) { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_pid_type='int' else $as_nop ac_pid_type='__int64' fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes then : else $as_nop printf "%s\n" "#define size_t unsigned int" >>confdefs.h fi ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" case $ac_cv_c_uint16_t in #( no|yes) ;; #( *) printf "%s\n" "#define uint16_t $ac_cv_c_uint16_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" case $ac_cv_c_uint32_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT32_T 1" >>confdefs.h printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" case $ac_cv_c_uint64_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT64_T 1" >>confdefs.h printf "%s\n" "#define uint64_t $ac_cv_c_uint64_t" >>confdefs.h ;; esac ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" case $ac_cv_c_uint8_t in #( no|yes) ;; #( *) printf "%s\n" "#define _UINT8_T 1" >>confdefs.h printf "%s\n" "#define uint8_t $ac_cv_c_uint8_t" >>confdefs.h ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 printf %s "checking for an ANSI C-conforming const... " >&6; } if test ${ac_cv_c_const+y} then : printf %s "(cached) " >&6 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { #ifndef __cplusplus /* Ultrix mips cc rejects this sort of thing. */ typedef int charset[2]; const charset cs = { 0, 0 }; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; /* NEC SVR4.0.2 mips cc rejects this. */ struct point {int x, y;}; static struct point const zero = {0,0}; /* IBM XL C 1.02.0.0 rejects this. It does not let you subtract one const X* pointer from another in an arm of an if-expression whose if-part is not a constant expression */ const char *g = "string"; pcpcc = &g + (g ? g-g : 0); /* HPUX 7.0 cc rejects these. */ ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; { /* SCO 3.2v4 cc rejects this sort of thing. */ char tx; char *t = &tx; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; if (s) return 0; } { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ int x[] = {25, 17}; const int *foo = &x[0]; ++foo; } { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ typedef const int *iptr; iptr p = 0; ++p; } { /* IBM XL C 1.02.0.0 rejects this sort of thing, saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ struct s { int j; const int *ap[3]; } bx; struct s *b = &bx; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; if (!foo) return 0; } return !cs[0] && !zero.x; #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : ac_cv_c_const=yes else $as_nop ac_cv_c_const=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 printf "%s\n" "$ac_cv_c_const" >&6; } if test $ac_cv_c_const = no; then printf "%s\n" "#define const /**/" >>confdefs.h fi # Checks for library functions. ac_func= for ac_item in $ac_func_c_list do if test $ac_func; then ac_fn_c_check_func "$LINENO" $ac_func ac_cv_func_$ac_func if eval test \"x\$ac_cv_func_$ac_func\" = xyes; then echo "#define $ac_item 1" >> confdefs.h fi ac_func= else ac_func=$ac_item fi done if test "x$ac_cv_func_fork" = xyes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 printf %s "checking for working fork... " >&6; } if test ${ac_cv_func_fork_works+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : ac_cv_func_fork_works=cross else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main (void) { /* By Ruediger Kuhlmann. */ return fork () < 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_fork_works=yes else $as_nop ac_cv_func_fork_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5 printf "%s\n" "$ac_cv_func_fork_works" >&6; } else ac_cv_func_fork_works=$ac_cv_func_fork fi if test "x$ac_cv_func_fork_works" = xcross; then case $host in *-*-amigaos* | *-*-msdosdjgpp*) # Override, as these systems have only a dummy fork() stub ac_cv_func_fork_works=no ;; *) ac_cv_func_fork_works=yes ;; esac { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 printf "%s\n" "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} fi ac_cv_func_vfork_works=$ac_cv_func_vfork if test "x$ac_cv_func_vfork" = xyes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 printf %s "checking for working vfork... " >&6; } if test ${ac_cv_func_vfork_works+y} then : printf %s "(cached) " >&6 else $as_nop if test "$cross_compiling" = yes then : ac_cv_func_vfork_works=cross else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Thanks to Paul Eggert for this test. */ $ac_includes_default #include #include #ifdef HAVE_VFORK_H # include #endif static void do_nothing (int sig) { (void) sig; } /* On some sparc systems, changes by the child to local and incoming argument registers are propagated back to the parent. The compiler is told about this with #include , but some compilers (e.g. gcc -O) don't grok . Test for this by using a static variable whose address is put into a register that is clobbered by the vfork. */ static void sparc_address_test (int arg) { static pid_t child; if (!child) { child = vfork (); if (child < 0) { perror ("vfork"); _exit(2); } if (!child) { arg = getpid(); write(-1, "", 0); _exit (arg); } } } int main (void) { pid_t parent = getpid (); pid_t child; sparc_address_test (0); /* On Solaris 2.4, changes by the child to the signal handler also munge signal handlers in the parent. To detect this, start by putting the parent's handler in a known state. */ signal (SIGTERM, SIG_DFL); child = vfork (); if (child == 0) { /* Here is another test for sparc vfork register problems. This test uses lots of local variables, at least as many local variables as main has allocated so far including compiler temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should reuse the register of parent for one of the local variables, since it will think that parent can't possibly be used any more in this routine. Assigning to the local variable will thus munge parent in the parent process. */ pid_t p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); /* Convince the compiler that p..p7 are live; otherwise, it might use the same hardware register for all 8 local variables. */ if (p != p1 || p != p2 || p != p3 || p != p4 || p != p5 || p != p6 || p != p7) _exit(1); /* Alter the child's signal handler. */ if (signal (SIGTERM, do_nothing) != SIG_DFL) _exit(1); /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent from child file descriptors. If the child closes a descriptor before it execs or exits, this munges the parent's descriptor as well. Test for this by closing stdout in the child. */ _exit(close(fileno(stdout)) != 0); } else { int status; struct stat st; while (wait(&status) != child) ; return ( /* Was there some problem with vforking? */ child < 0 /* Did the child munge the parent's signal handler? */ || signal (SIGTERM, SIG_DFL) != SIG_DFL /* Did the child fail? (This shouldn't happen.) */ || status /* Did the vfork/compiler bug occur? */ || parent != getpid() /* Did the file descriptor bug occur? */ || fstat(fileno(stdout), &st) != 0 ); } } _ACEOF if ac_fn_c_try_run "$LINENO" then : ac_cv_func_vfork_works=yes else $as_nop ac_cv_func_vfork_works=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5 printf "%s\n" "$ac_cv_func_vfork_works" >&6; } fi; if test "x$ac_cv_func_fork_works" = xcross; then ac_cv_func_vfork_works=$ac_cv_func_vfork { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 printf "%s\n" "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} fi if test "x$ac_cv_func_vfork_works" = xyes; then printf "%s\n" "#define HAVE_WORKING_VFORK 1" >>confdefs.h else printf "%s\n" "#define vfork fork" >>confdefs.h fi if test "x$ac_cv_func_fork_works" = xyes; then printf "%s\n" "#define HAVE_WORKING_FORK 1" >>confdefs.h fi # We don't want the following two, since autoconf, if malloc(0) returns NULL, refines malloc as rpl_malloc # and we have to provide our own rpl_malloc() and likewise rpl_realloc() functions. # keepalived doesn't do 0 length malloc()s so it is not an issue. # We add malloc and realloc to AC_CHECK_FUNCS instead. #AC_FUNC_MALLOC #AC_FUNC_REALLOC ac_fn_c_check_func "$LINENO" "dup2" "ac_cv_func_dup2" if test "x$ac_cv_func_dup2" = xyes then : printf "%s\n" "#define HAVE_DUP2 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "dup3" "ac_cv_func_dup3" if test "x$ac_cv_func_dup3" = xyes then : printf "%s\n" "#define HAVE_DUP3 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "getcwd" "ac_cv_func_getcwd" if test "x$ac_cv_func_getcwd" = xyes then : printf "%s\n" "#define HAVE_GETCWD 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "gettimeofday" "ac_cv_func_gettimeofday" if test "x$ac_cv_func_gettimeofday" = xyes then : printf "%s\n" "#define HAVE_GETTIMEOFDAY 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "malloc" "ac_cv_func_malloc" if test "x$ac_cv_func_malloc" = xyes then : printf "%s\n" "#define HAVE_MALLOC 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memmove" "ac_cv_func_memmove" if test "x$ac_cv_func_memmove" = xyes then : printf "%s\n" "#define HAVE_MEMMOVE 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "memset" "ac_cv_func_memset" if test "x$ac_cv_func_memset" = xyes then : printf "%s\n" "#define HAVE_MEMSET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "realloc" "ac_cv_func_realloc" if test "x$ac_cv_func_realloc" = xyes then : printf "%s\n" "#define HAVE_REALLOC 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "select" "ac_cv_func_select" if test "x$ac_cv_func_select" = xyes then : printf "%s\n" "#define HAVE_SELECT 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv" if test "x$ac_cv_func_setenv" = xyes then : printf "%s\n" "#define HAVE_SETENV 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "socket" "ac_cv_func_socket" if test "x$ac_cv_func_socket" = xyes then : printf "%s\n" "#define HAVE_SOCKET 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strcasecmp" "ac_cv_func_strcasecmp" if test "x$ac_cv_func_strcasecmp" = xyes then : printf "%s\n" "#define HAVE_STRCASECMP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strchr" "ac_cv_func_strchr" if test "x$ac_cv_func_strchr" = xyes then : printf "%s\n" "#define HAVE_STRCHR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strdup" "ac_cv_func_strdup" if test "x$ac_cv_func_strdup" = xyes then : printf "%s\n" "#define HAVE_STRDUP 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror" if test "x$ac_cv_func_strerror" = xyes then : printf "%s\n" "#define HAVE_STRERROR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strpbrk" "ac_cv_func_strpbrk" if test "x$ac_cv_func_strpbrk" = xyes then : printf "%s\n" "#define HAVE_STRPBRK 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strstr" "ac_cv_func_strstr" if test "x$ac_cv_func_strstr" = xyes then : printf "%s\n" "#define HAVE_STRSTR 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtol" "ac_cv_func_strtol" if test "x$ac_cv_func_strtol" = xyes then : printf "%s\n" "#define HAVE_STRTOL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "strtoul" "ac_cv_func_strtoul" if test "x$ac_cv_func_strtoul" = xyes then : printf "%s\n" "#define HAVE_STRTOUL 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "uname" "ac_cv_func_uname" if test "x$ac_cv_func_uname" = xyes then : printf "%s\n" "#define HAVE_UNAME 1" >>confdefs.h fi for ac_func in vsyslog do : ac_fn_c_check_func "$LINENO" "vsyslog" "ac_cv_func_vsyslog" if test "x$ac_cv_func_vsyslog" = xyes then : printf "%s\n" "#define HAVE_VSYSLOG 1" >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS VSYSLOG" fi done for ac_func in memfd_create do : ac_fn_c_check_func "$LINENO" "memfd_create" "ac_cv_func_memfd_create" if test "x$ac_cv_func_memfd_create" = xyes then : printf "%s\n" "#define HAVE_MEMFD_CREATE 1" >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS MEMFD_CREATE" fi done { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } if test ${ac_cv_c_undeclared_builtin_options+y} then : printf %s "(cached) " >&6 else $as_nop ac_save_CFLAGS=$CFLAGS ac_cv_c_undeclared_builtin_options='cannot detect' for ac_arg in '' -fno-builtin; do CFLAGS="$ac_save_CFLAGS $ac_arg" # This test program should *not* compile successfully. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main (void) { (void) strchr; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop # This test program should compile successfully. # No library function is consistently available on # freestanding implementations, so test against a dummy # declaration. Include always-available headers on the # off chance that they somehow elicit warnings. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include extern void ac_decl (int, char *); int main (void) { (void) ac_decl (0, (char *) 0); (void) ac_decl; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : if test x"$ac_arg" = x then : ac_cv_c_undeclared_builtin_options='none needed' else $as_nop ac_cv_c_undeclared_builtin_options=$ac_arg fi break fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext done CFLAGS=$ac_save_CFLAGS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } case $ac_cv_c_undeclared_builtin_options in #( 'cannot detect') : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot make $CC report undeclared builtins See \`config.log' for more details" "$LINENO" 5; } ;; #( 'none needed') : ac_c_undeclared_builtin_options='' ;; #( *) : ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; esac if test "x$ac_cv_func_memfd_create" != xyes then : ac_fn_check_decl "$LINENO" "__NR_memfd_create" "ac_cv_have_decl___NR_memfd_create" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl___NR_memfd_create" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL___NR_MEMFD_CREATE $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : printf "%s\n" "#define USE_MEMFD_CREATE_SYSCALL 1 " >>confdefs.h fi fi ac_fn_check_decl "$LINENO" "O_TMPFILE" "ac_cv_have_decl_O_TMPFILE" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_O_TMPFILE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_O_TMPFILE $ac_have_decl" >>confdefs.h # glibc uses unsigned int as 3rd parameter to __assert_fail(), musl uses int. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include void __assert_fail(const char * a, const char *b, unsigned int l, const char *c) { exit(a[0] + b[0] + c[0] + l == 0); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : LINE_type="unsigned int" else $as_nop LINE_type="int" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext printf "%s\n" "#define LINE_type $LINE_type " >>confdefs.h # If sizeof(time_t) is only 4 bytes, then adding two times after Sat 10 Jan 13:37:04 GMT 2004 # results in overflow. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { /* This is a bit of a trick. If sizeof(time_t) <= 4, then the size of the array is negative, giving a compile error */ char arr[(int)sizeof(time_t) - 5]; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop printf "%s\n" "#define TIME_T_ADD_OVERFLOWS 1 " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext SAV_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wattributes -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include static __always_inline int test_func(int val) { return val; } int main(void) { int val = test_func(3); return val; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ static __inline __attribute__ ((__always_inline__)) int test_func(int val) { return val; } int main(void) { int val = test_func(3); return val; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define __always_inline __inline __attribute__ ((__always_inline__))" >>confdefs.h else $as_nop printf "%s\n" "#define __always_inline inline" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$SAV_CFLAGS" if test "$enable_dynamic_linking" then : if test .$enable_vrrp != .no then : enable_libiptc_dynamic=$enable_dynamic_linking enable_libipset_dynamic=$enable_dynamic_linking fi if test .$enable_lvs != .no then : enable_libnl_dynamic=$enable_dynamic_linking fi fi # check for missing definition - added in glibc 2.8 ac_fn_check_decl "$LINENO" "ETHERTYPE_IPV6" "ac_cv_have_decl_ETHERTYPE_IPV6" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_ETHERTYPE_IPV6" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_ETHERTYPE_IPV6 $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop printf "%s\n" "#define ETHERTYPE_IPV6 0x86dd" >>confdefs.h fi # IPV6_FREEBIND - added Linux v4.15 if test .$enable_vrrp != .no then : ac_fn_check_decl "$LINENO" "IPV6_FREEBIND" "ac_cv_have_decl_IPV6_FREEBIND" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPV6_FREEBIND" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPV6_FREEBIND $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPV6_FREEBIND" fi fi # IPV6_MULTICAST_ALL - added Linux v4.20 ac_fn_check_decl "$LINENO" "IPV6_MULTICAST_ALL" "ac_cv_have_decl_IPV6_MULTICAST_ALL" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPV6_MULTICAST_ALL" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPV6_MULTICAST_ALL $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPV6_MULTICAST_ALL" # We can't include before otherwise there are a lot of # duplicate definitions. If we include them the other way around and # doesn't define IPV6_MULTICAST_ALL, then we don't get the definition from # due to the UAPI guards to stop compile errors. # IPV6_MULTICAST_ALL is defined in , and if it isn't defined in # we must create our own definition. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { int val = IPV6_MULTICAST_ALL; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop # Find the defined value of IPV6_MULTICAST_ALL INCLUDE_FILES=`echo -e "#include \n int main(void) { int var = IPV6_MULTICAST_ALL; }" | $CC -H -E - -o/dev/null 2>&1 | sed -e "s/^[.]*//" | grep "^ "` IPV6_MCA=`grep "#define.*IPV6_MULTICAST_ALL" $INCLUDE_FILES | sed -e "s/.*IPV6_MULTICAST_ALL[[ \t]]*//"` printf "%s\n" "#define IPV6_MULTICAST_ALL $IPV6_MCA" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi # check for openssl headers NEED_EVP=no NEED_SSL=no if test "$enable_vrrp" != no -a \ "$enable_vrrp_auth" != no; then NEED_EVP=yes fi if test "$enable_lvs" != no; then NEED_EVP=yes NEED_SSL=yes fi for ac_header in openssl/ssl.h openssl/err.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop if test $NEED_SSL = yes; then as_fn_error $? " !!! OpenSSL is not properly installed on your system. !!! !!! Can not include OpenSSL headers files. !!!" "$LINENO" 5 fi fi done for ac_header in openssl/md5.h openssl/evp.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop if test $NEED_EVP = yes; then as_fn_error $? " !!! OpenSSL is not properly installed on your system. !!! !!! Can not include OpenSSL EVP headers files. !!!" "$LINENO" 5 fi fi done unset LIBS $PKG_CONFIG --exists openssl if test $? -eq 0; then if test -n "OPENSSL"; then KA_PKG_PFX=OPENSSL else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I openssl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other openssl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires openssl` var=`$PKG_CONFIG --libs-only-l openssl` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs openssl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi else OPENSSL_LIBS="-lssl -lcrypto" fi EXTRA_LIBS=`echo $OPENSSL_LIBS | sed -e "s/-lssl//"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for SSL_CTX_new in -lssl" >&5 printf %s "checking for SSL_CTX_new in -lssl... " >&6; } if test ${ac_cv_lib_ssl_SSL_CTX_new+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lssl $EXTRA_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char SSL_CTX_new (); int main (void) { return SSL_CTX_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_ssl_SSL_CTX_new=yes else $as_nop ac_cv_lib_ssl_SSL_CTX_new=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_CTX_new" >&5 printf "%s\n" "$ac_cv_lib_ssl_SSL_CTX_new" >&6; } if test "x$ac_cv_lib_ssl_SSL_CTX_new" = xyes then : printf "%s\n" "#define HAVE_LIBSSL 1" >>confdefs.h LIBS="-lssl $LIBS" else $as_nop if test $NEED_SSL = yes; then as_fn_error $? "OpenSSL libraries are required" "$LINENO" 5 fi fi if test $NEED_SSL = yes; then KA_LIBS="$KA_LIBS $LIBS" fi unset LIBS EXTRA_LIBS=`echo $OPENSSL_LIBS | sed -e "s/-lcrypto//"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for EVP_DigestInit_ex in -lcrypto" >&5 printf %s "checking for EVP_DigestInit_ex in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_EVP_DigestInit_ex+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $EXTRA_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char EVP_DigestInit_ex (); int main (void) { return EVP_DigestInit_ex (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypto_EVP_DigestInit_ex=yes else $as_nop ac_cv_lib_crypto_EVP_DigestInit_ex=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_EVP_DigestInit_ex" >&5 printf "%s\n" "$ac_cv_lib_crypto_EVP_DigestInit_ex" >&6; } if test "x$ac_cv_lib_crypto_EVP_DigestInit_ex" = xyes then : printf "%s\n" "#define HAVE_LIBCRYPTO 1" >>confdefs.h LIBS="-lcrypto $LIBS" else $as_nop if test $NEED_EVP = yes; then as_fn_error $? "OpenSSL EVP libraries are required" "$LINENO" 5 fi fi if test $NEED_EVP = yes; then KA_LIBS="$KA_LIBS $LIBS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for EVP_MD_CTX_new in -lcrypto" >&5 printf %s "checking for EVP_MD_CTX_new in -lcrypto... " >&6; } if test ${ac_cv_lib_crypto_EVP_MD_CTX_new+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $EXTRA_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char EVP_MD_CTX_new (); int main (void) { return EVP_MD_CTX_new (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_crypto_EVP_MD_CTX_new=yes else $as_nop ac_cv_lib_crypto_EVP_MD_CTX_new=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_EVP_MD_CTX_new" >&5 printf "%s\n" "$ac_cv_lib_crypto_EVP_MD_CTX_new" >&6; } if test "x$ac_cv_lib_crypto_EVP_MD_CTX_new" = xyes then : printf "%s\n" "#define HAVE_LIBCRYPTO 1" >>confdefs.h LIBS="-lcrypto $LIBS" else $as_nop printf "%s\n" "#define EVP_MD_CTX_new() EVP_MD_CTX_create()" >>confdefs.h printf "%s\n" "#define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx)" >>confdefs.h printf "%s\n" "#define EVP_MD_CTX_reset(ctx) EVP_MD_CTX_init(ctx)" >>confdefs.h fi fi unset LIBS # Introduced in OpenSSL ver 0.9.9 LIBS=$OPENSSL_LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking SSL_set_tlsext_host_name() - may be a definition" >&5 printf %s "checking SSL_set_tlsext_host_name() - may be a definition... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { request_t req; SSL_set_tlsext_host_name(req.ssl, "SSL"); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_SSL_SET_TLSEXT_HOST_NAME_ 1 " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext # SSL_CTX_set_verify_depth() introduced OpenSSL v0.9.5a ac_fn_c_check_func "$LINENO" "SSL_CTX_set_verify_depth" "ac_cv_func_SSL_CTX_set_verify_depth" if test "x$ac_cv_func_SSL_CTX_set_verify_depth" = xyes then : printf "%s\n" "#define HAVE_SSL_CTX_SET_VERIFY_DEPTH 1" >>confdefs.h fi # SSL_set0_rbio(), SSL_set0_wbio() OPENSSL_init_crypto() and TLS_method() introduced OpenSSL v1.1.0 ac_fn_c_check_func "$LINENO" "SSL_set0_rbio" "ac_cv_func_SSL_set0_rbio" if test "x$ac_cv_func_SSL_set0_rbio" = xyes then : printf "%s\n" "#define HAVE_SSL_SET0_RBIO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "SSL_set0_wbio" "ac_cv_func_SSL_set0_wbio" if test "x$ac_cv_func_SSL_set0_wbio" = xyes then : printf "%s\n" "#define HAVE_SSL_SET0_WBIO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "OPENSSL_init_crypto" "ac_cv_func_OPENSSL_init_crypto" if test "x$ac_cv_func_OPENSSL_init_crypto" = xyes then : printf "%s\n" "#define HAVE_OPENSSL_INIT_CRYPTO 1" >>confdefs.h fi ac_fn_c_check_func "$LINENO" "TLS_method" "ac_cv_func_TLS_method" if test "x$ac_cv_func_TLS_method" = xyes then : printf "%s\n" "#define HAVE_TLS_METHOD 1" >>confdefs.h fi # ERR_get_error_all introduced in OpenSSL v3.0 ac_fn_c_check_func "$LINENO" "ERR_get_error_all" "ac_cv_func_ERR_get_error_all" if test "x$ac_cv_func_ERR_get_error_all" = xyes then : printf "%s\n" "#define HAVE_ERR_GET_ERROR_ALL 1" >>confdefs.h fi # In OpenSSL v1.1.1 the call to SSL_CTX_new() fails if OPENSSL_init_crypto() has been called with # OPENSSL_INIT_NO_LOAD_CONFIG. It does not fail in v1.1.0h and v1.1.1b. if test .$ac_cv_func_OPENSSL_init_crypto = .yes then : if test .$ac_cv_func_TLS_method = .yes then : method_func=TLS_method else $as_nop method_func=SSLv23_method fi if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Cannot determine if need to OPENSSL_init_crypto() problem. Assuming yes for safety." >&5 printf "%s\n" "$as_me: WARNING: Cannot determine if need to OPENSSL_init_crypto() problem. Assuming yes for safety." >&2;} openssl_init_no_load_bug=1 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { const SSL_METHOD *meth; SSL_CTX *ctx; if (!OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG, NULL)) return 1; /* Initialize SSL context */ meth = $method_func(); if (!(ctx = SSL_CTX_new(meth))) return 1; return 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : openssl_init_no_load_bug=0 else $as_nop openssl_init_no_load_bug=1 fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $openssl_init_no_load_bug -eq 1 then : printf "%s\n" "#define HAVE_OPENSSL_INIT_NO_LOAD_CONFIG_BUG 1 " >>confdefs.h fi fi unset LIBS $PKG_CONFIG --exists libkmod if test $? -eq 0 then : printf "%s\n" "#define _HAVE_LIBKMOD_ 1 " >>confdefs.h if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libkmod`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libkmod`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libkmod` var=`$PKG_CONFIG --libs-only-l libkmod` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libkmod`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi SYSTEM_OPTIONS="$SYSTEM_OPTIONS LIBKMOD" fi IPV4_DEVCONF=No if test .$enable_vrrp != .no; then SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" IPV4_DEVCONF=Yes ac_fn_check_decl "$LINENO" "IPV4_DEVCONF_ARP_IGNORE" "ac_cv_have_decl_IPV4_DEVCONF_ARP_IGNORE" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPV4_DEVCONF_ARP_IGNORE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPV4_DEVCONF_ARP_IGNORE $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPV4_DEVCONF=No break fi ac_fn_check_decl "$LINENO" "IPV4_DEVCONF_ACCEPT_LOCAL" "ac_cv_have_decl_IPV4_DEVCONF_ACCEPT_LOCAL" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPV4_DEVCONF_ACCEPT_LOCAL" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPV4_DEVCONF_ACCEPT_LOCAL $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPV4_DEVCONF=No break fi ac_fn_check_decl "$LINENO" "IPV4_DEVCONF_RP_FILTER" "ac_cv_have_decl_IPV4_DEVCONF_RP_FILTER" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPV4_DEVCONF_RP_FILTER" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPV4_DEVCONF_RP_FILTER $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPV4_DEVCONF=No break fi ac_fn_check_decl "$LINENO" "IPV4_DEVCONF_ARPFILTER" "ac_cv_have_decl_IPV4_DEVCONF_ARPFILTER" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPV4_DEVCONF_ARPFILTER" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPV4_DEVCONF_ARPFILTER $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPV4_DEVCONF=No break fi if test $IPV4_DEVCONF = Yes; then printf "%s\n" "#define _HAVE_IPV4_DEVCONF_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPV4_DEVCONF" fi for ac_header in linux/rtnetlink.h do : ac_fn_c_check_header_compile "$LINENO" "linux/rtnetlink.h" "ac_cv_header_linux_rtnetlink_h" "$RTNETLINK_EXTRA_INCLUDE " if test "x$ac_cv_header_linux_rtnetlink_h" = xyes then : printf "%s\n" "#define HAVE_LINUX_RTNETLINK_H 1" >>confdefs.h else $as_nop as_fn_error $? "Unusable linux/rtnetlink.h" "$LINENO" 5 fi done CPPFLAGS="$SAV_CPPFLAGS" fi NETLINK_VER=0 IPVS_USE_NL=No if test .$enable_lvs != .no -a .${enable_libnl} != .no; then $PKG_CONFIG --exists libnl-3.0 if test $? -eq 0; then if test -n "NL3"; then KA_PKG_PFX=NL3 else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test .remove-requires = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libnl-3.0` var=`$PKG_CONFIG --libs-only-l libnl-3.0` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libnl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi as_ac_Lib=`printf "%s\n" "ac_cv_lib_$NL3_LIB_NAMES""_nl_socket_alloc" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for nl_socket_alloc in -l$NL3_LIB_NAMES" >&5 printf %s "checking for nl_socket_alloc in -l$NL3_LIB_NAMES... " >&6; } if eval test \${$as_ac_Lib+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-l$NL3_LIB_NAMES $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char nl_socket_alloc (); int main (void) { return nl_socket_alloc (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$as_ac_Lib=yes" else $as_nop eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes" then : NETLINK_VER=3 printf "%s\n" "#define _HAVE_LIBNL3_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS LIBNL3" if test .$enable_libnl_dynamic = .yes; then SYSTEM_OPTIONS="$SYSTEM_OPTIONS LIBNL_DYNAMIC" if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" printf "%s\n" "#define _LIBNL_DYNAMIC_ 1 " >>confdefs.h NEED_LIBDL=Yes if test $LDD = :; then as_fn_error $? "ldd is required for dynamic run-time linking support" "$LINENO" 5 fi SAV_LIBS="$LIBS" LIBS="$LIBS -l$NL3_LIB_NAMES" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern void nl_socket_alloc(void); int main(void) { nl_socket_alloc(); return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : LIB_DETAILS=`$LDD ./conftest$EXEEXT | grep $NL3_LIB_NAMES.so | sed -e "s/^[ \t]*//"` LIB_NAME=`echo $LIB_DETAILS | sed -e "s/ .*//"` fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS="$SAV_LIBS" printf "%s\n" "#define NL3_LIB_NAME \"$LIB_NAME\" " >>confdefs.h else if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libnl-3.0` var=`$PKG_CONFIG --libs-only-l libnl-3.0` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libnl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi fi if test -n "GENL"; then KA_PKG_PFX=GENL else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnl-genl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnl-genl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test .remove-requires = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libnl-genl-3.0` var=`$PKG_CONFIG --libs-only-l libnl-genl-3.0` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libnl-genl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi as_ac_Lib=`printf "%s\n" "ac_cv_lib_$GENL_LIB_NAMES""_genl_connect" | $as_tr_sh` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for genl_connect in -l$GENL_LIB_NAMES" >&5 printf %s "checking for genl_connect in -l$GENL_LIB_NAMES... " >&6; } if eval test \${$as_ac_Lib+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-l$GENL_LIB_NAMES $NL3_LIBS $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char genl_connect (); int main (void) { return genl_connect (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : eval "$as_ac_Lib=yes" else $as_nop eval "$as_ac_Lib=no" fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi eval ac_res=\$$as_ac_Lib { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 printf "%s\n" "$ac_res" >&6; } if eval test \"x\$"$as_ac_Lib"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_LIB$GENL_LIB_NAMES" | $as_tr_cpp` 1 _ACEOF LIBS="-l$GENL_LIB_NAMES $LIBS" else $as_nop as_fn_error $? "libnl-3 is installed but not libnl-gen-3. Please, install libnl-gen-3/libnl-genl-3." "$LINENO" 5 fi IPVS_USE_NL=Yes if test .$enable_libnl_dynamic = .yes; then if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnl-genl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnl-genl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test $LDD = :; then as_fn_error $? "ldd is required for dynamic run-time linking support" "$LINENO" 5 fi SAV_LIBS="$LIBS" LIBS="$LIBS -l$GENL_LIB_NAMES" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern void genl_connect(void); int main(void) { genl_connect(); return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : LIB_DETAILS=`$LDD ./conftest$EXEEXT | grep $GENL_LIB_NAMES.so | sed -e "s/^[ \t]*//"` LIB_NAME=`echo $LIB_DETAILS | sed -e "s/ .*//"` fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS="$SAV_LIBS" printf "%s\n" "#define NL3_GENL_LIB_NAME \"$LIB_NAME\" " >>confdefs.h else if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnl-genl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnl-genl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libnl-genl-3.0` var=`$PKG_CONFIG --libs-only-l libnl-genl-3.0` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libnl-genl-3.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi fi fi fi if test $NETLINK_VER -eq 0; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for nl_socket_modify_cb in -lnl" >&5 printf %s "checking for nl_socket_modify_cb in -lnl... " >&6; } if test ${ac_cv_lib_nl_nl_socket_modify_cb+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lnl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char nl_socket_modify_cb (); int main (void) { return nl_socket_modify_cb (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_nl_nl_socket_modify_cb=yes else $as_nop ac_cv_lib_nl_nl_socket_modify_cb=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nl_nl_socket_modify_cb" >&5 printf "%s\n" "$ac_cv_lib_nl_nl_socket_modify_cb" >&6; } if test "x$ac_cv_lib_nl_nl_socket_modify_cb" = xyes then : IPVS_USE_NL=Yes NETLINK_VER=1 printf "%s\n" "#define _HAVE_LIBNL1_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS LIBNL1" if test .$enable_libnl_dynamic = .yes; then if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnl-1`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnl-1`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" CONFIG_OPTIONS="$CONFIG_OPTIONS LIBNL_DYNAMIC" printf "%s\n" "#define _LIBNL_DYNAMIC_ 1 " >>confdefs.h NEED_LIBDL=Yes if test $LDD = :; then as_fn_error $? "ldd is required for dynamic run-time linking support" "$LINENO" 5 fi SAV_LIBS="$LIBS" LIBS="$LIBS -lnl" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern void nl_socket_modify_cb(void); int main(void) { nl_socket_modify_cb(); return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : LIB_DETAILS=`$LDD ./conftest$EXEEXT | grep nl.so | sed -e "s/^[ \t]*//"` LIB_NAME=`echo $LIB_DETAILS | sed -e "s/ .*//"` fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS="$SAV_LIBS" printf "%s\n" "#define NL_LIB_NAME \"$LIB_NAME\" " >>confdefs.h else if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnl-1`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnl-1`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libnl-1` var=`$PKG_CONFIG --libs-only-l libnl-1` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libnl-1`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi fi else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: keepalived will be built without libnl support." >&5 printf "%s\n" "$as_me: WARNING: keepalived will be built without libnl support." >&2;} fi fi if test $NETLINK_VER -ne 0; then SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernel_inc $NL3_CPPFLAGS" for ac_header in netlink/netlink.h do : ac_fn_c_check_header_compile "$LINENO" "netlink/netlink.h" "ac_cv_header_netlink_netlink_h" "$ac_includes_default" if test "x$ac_cv_header_netlink_netlink_h" = xyes then : printf "%s\n" "#define HAVE_NETLINK_NETLINK_H 1" >>confdefs.h else $as_nop as_fn_error $? "netlink headers missing" "$LINENO" 5 fi done for ac_header in netlink/genl/ctrl.h netlink/genl/genl.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop as_fn_error $? "netlink genl headers missing" "$LINENO" 5 fi done CPPFLAGS="$SAV_CPPFLAGS" fi fi if test $NETLINK_VER -eq 1; then LIBNL1_TRUE= LIBNL1_FALSE='#' else LIBNL1_TRUE='#' LIBNL1_FALSE= fi if test $NETLINK_VER -eq 3; then LIBNL3_TRUE= LIBNL3_FALSE='#' else LIBNL3_TRUE='#' LIBNL3_FALSE= fi if test .$enable_lvs != .no -a .$enable_libnl_dynamic = .yes -a $NETLINK_VER -ne 0; then LIBNL_DYNAMIC_TRUE= LIBNL_DYNAMIC_FALSE='#' else LIBNL_DYNAMIC_TRUE='#' LIBNL_DYNAMIC_FALSE= fi unset LIBS MAGIC=0 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for magic_open in -lmagic" >&5 printf %s "checking for magic_open in -lmagic... " >&6; } if test ${ac_cv_lib_magic_magic_open+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lmagic $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char magic_open (); int main (void) { return magic_open (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_magic_magic_open=yes else $as_nop ac_cv_lib_magic_magic_open=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_magic_magic_open" >&5 printf "%s\n" "$ac_cv_lib_magic_magic_open" >&6; } if test "x$ac_cv_lib_magic_magic_open" = xyes then : printf "%s\n" "#define _HAVE_LIBMAGIC_ 1 " >>confdefs.h KA_LIBS="$KA_LIBS -lmagic" MAGIC=1 fi if test $MAGIC -eq 1; then MAGIC_TRUE= MAGIC_FALSE='#' else MAGIC_TRUE='#' MAGIC_FALSE= fi unset LIBS ac_fn_check_decl "$LINENO" "RTA_ENCAP" "ac_cv_have_decl_RTA_ENCAP" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTA_ENCAP" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTA_ENCAP $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTA_EXPIRES" "ac_cv_have_decl_RTA_EXPIRES" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTA_EXPIRES" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTA_EXPIRES $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTA_NEWDST" "ac_cv_have_decl_RTA_NEWDST" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTA_NEWDST" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTA_NEWDST $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTA_PREF" "ac_cv_have_decl_RTA_PREF" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTA_PREF" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTA_PREF $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_SUPPRESS_PREFIXLEN" "ac_cv_have_decl_FRA_SUPPRESS_PREFIXLEN" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_SUPPRESS_PREFIXLEN" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_SUPPRESS_PREFIXLEN $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_SUPPRESS_IFGROUP" "ac_cv_have_decl_FRA_SUPPRESS_IFGROUP" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_SUPPRESS_IFGROUP" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_SUPPRESS_IFGROUP $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_TUN_ID" "ac_cv_have_decl_FRA_TUN_ID" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_TUN_ID" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_TUN_ID $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTAX_CC_ALGO" "ac_cv_have_decl_RTAX_CC_ALGO" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTAX_CC_ALGO" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTAX_CC_ALGO $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTAX_QUICKACK" "ac_cv_have_decl_RTAX_QUICKACK" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTAX_QUICKACK" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTAX_QUICKACK $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTEXT_FILTER_SKIP_STATS" "ac_cv_have_decl_RTEXT_FILTER_SKIP_STATS" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTEXT_FILTER_SKIP_STATS" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTEXT_FILTER_SKIP_STATS $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_L3MDEV" "ac_cv_have_decl_FRA_L3MDEV" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_L3MDEV" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_L3MDEV $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_UID_RANGE" "ac_cv_have_decl_FRA_UID_RANGE" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_UID_RANGE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_UID_RANGE $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTAX_FASTOPEN_NO_COOKIE" "ac_cv_have_decl_RTAX_FASTOPEN_NO_COOKIE" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTAX_FASTOPEN_NO_COOKIE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTAX_FASTOPEN_NO_COOKIE $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTA_VIA" "ac_cv_have_decl_RTA_VIA" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTA_VIA" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTA_VIA $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_PROTOCOL" "ac_cv_have_decl_FRA_PROTOCOL" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_PROTOCOL" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_PROTOCOL $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_IP_PROTO" "ac_cv_have_decl_FRA_IP_PROTO" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_IP_PROTO" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_IP_PROTO $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_SPORT_RANGE" "ac_cv_have_decl_FRA_SPORT_RANGE" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_SPORT_RANGE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_SPORT_RANGE $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "FRA_DPORT_RANGE" "ac_cv_have_decl_FRA_DPORT_RANGE" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_FRA_DPORT_RANGE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_FRA_DPORT_RANGE $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "RTA_TTL_PROPAGATE" "ac_cv_have_decl_RTA_TTL_PROPAGATE" "$RTNETLINK_EXTRA_INCLUDES #include #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_RTA_TTL_PROPAGATE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_RTA_TTL_PROPAGATE $ac_have_decl" >>confdefs.h for flag in RTA_ENCAP RTA_EXPIRES RTA_NEWDST RTA_PREF FRA_SUPPRESS_PREFIXLEN FRA_SUPPRESS_IFGROUP FRA_TUN_ID RTAX_CC_ALGO RTAX_QUICKACK RTEXT_FILTER_SKIP_STATS FRA_L3MDEV FRA_UID_RANGE RTAX_FASTOPEN_NO_COOKIE RTA_VIA FRA_PROTOCOL FRA_IP_PROTO FRA_SPORT_RANGE FRA_DPORT_RANGE RTA_TTL_PROPAGATE; do eval decl_var=\$ac_cv_have_decl_$flag if test ${decl_var} = yes; then SYSTEM_OPTIONS="$SYSTEM_OPTIONS "${flag} fi done ac_fn_check_decl "$LINENO" "IFA_FLAGS" "ac_cv_have_decl_IFA_FLAGS" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IFA_FLAGS" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IFA_FLAGS $ac_have_decl" >>confdefs.h for flag in IFA_FLAGS; do eval decl_var=\$ac_cv_have_decl_$flag if test ${decl_var} = yes; then SYSTEM_OPTIONS="$SYSTEM_OPTIONS "${flag} fi done ac_fn_check_decl "$LINENO" "F_OFD_SETLK" "ac_cv_have_decl_F_OFD_SETLK" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_F_OFD_SETLK" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_F_OFD_SETLK $ac_have_decl" >>confdefs.h for flag in F_OFD_SETLK; do eval decl_var=\$ac_cv_have_decl_$flag if test ${decl_var} = yes; then SYSTEM_OPTIONS="$SYSTEM_OPTIONS "${flag} fi done ac_fn_check_decl "$LINENO" "IFA_PROTO" "ac_cv_have_decl_IFA_PROTO" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IFA_PROTO" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IFA_PROTO $ac_have_decl" >>confdefs.h for flag in IFA_PROTO; do eval decl_var=\$ac_cv_have_decl_$flag if test ${decl_var} = yes; then SYSTEM_OPTIONS="$SYSTEM_OPTIONS "${flag} fi done if test $ac_cv_have_decl_RTA_ENCAP = yes then : ac_fn_check_decl "$LINENO" "LWTUNNEL_ENCAP_MPLS" "ac_cv_have_decl_LWTUNNEL_ENCAP_MPLS" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_LWTUNNEL_ENCAP_MPLS" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_LWTUNNEL_ENCAP_MPLS $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "LWTUNNEL_ENCAP_ILA" "ac_cv_have_decl_LWTUNNEL_ENCAP_ILA" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_LWTUNNEL_ENCAP_ILA" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_LWTUNNEL_ENCAP_ILA $ac_have_decl" >>confdefs.h for flag in LWTUNNEL_ENCAP_MPLS LWTUNNEL_ENCAP_ILA; do eval decl_var=\$ac_cv_have_decl_$flag if test ${decl_var} = yes; then SYSTEM_OPTIONS="$SYSTEM_OPTIONS ${flag}" fi done fi USE_IPTABLES=No USE_LIBIPSET=No if test .$enable_iptables != .no then : USE_IPTABLES=Yes SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" for ac_header in libiptc/libip6tc.h libiptc/libiptc.h libiptc/libxtc.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop USE_IPTABLES=No break fi done CPPFLAGS="$SAV_CPPFLAGS" if test $USE_IPTABLES = Yes; then PKG_CONFIG_IP4TC=Yes $PKG_CONFIG --exists libip4tc if test $? -eq 0 then : if test -n "IP4TC"; then KA_PKG_PFX=IP4TC else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I --static libip4tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other --static libip4tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test .remove-requires = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires --static libip4tc` var=`$PKG_CONFIG --libs-only-l --static libip4tc` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs --static libip4tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi if test -n "IP6TC"; then KA_PKG_PFX=IP6TC else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I --static libip6tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other --static libip6tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test .remove-requires = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires --static libip6tc` var=`$PKG_CONFIG --libs-only-l --static libip6tc` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs --static libip6tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi IPTC_LIBS="$IP4TC_LIBS $IP6TC_LIBS" IPTC_LIB_NAMES="$IP4TC_LIB_NAMES $IP6TC_LIB_NAMES" else $as_nop PKG_CONFIG_IP4TC=No if test -n "IPTC"; then KA_PKG_PFX=IPTC else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I --static libiptc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other --static libiptc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test .remove-requires = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires --static libiptc` var=`$PKG_CONFIG --libs-only-l --static libiptc` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs --static libiptc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi fi SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing iptc_init" >&5 printf %s "checking for library containing iptc_init... " >&6; } if test ${ac_cv_search_iptc_init+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char iptc_init (); int main (void) { return iptc_init (); ; return 0; } _ACEOF for ac_lib in '' $IPTC_LIB_NAMES do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_iptc_init=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_iptc_init+y} then : break fi done if test ${ac_cv_search_iptc_init+y} then : else $as_nop ac_cv_search_iptc_init=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_iptc_init" >&5 printf "%s\n" "$ac_cv_search_iptc_init" >&6; } ac_res=$ac_cv_search_iptc_init if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" if test .${enable_libiptc_dynamic} != .yes; then if test $PKG_CONFIG_IP4TC = Yes then : if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I --static libip4tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other --static libip4tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires --static libip4tc` var=`$PKG_CONFIG --libs-only-l --static libip4tc` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs --static libip4tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I --static libip6tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other --static libip6tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires --static libip6tc` var=`$PKG_CONFIG --libs-only-l --static libip6tc` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs --static libip6tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi else $as_nop if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I --static libiptc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other --static libiptc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires --static libiptc` var=`$PKG_CONFIG --libs-only-l --static libiptc` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs --static libiptc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi fi KA_LIBS=`echo $KA_LIBS | sed -e "s/ -liptc//"` IPTC_LIBS=`echo $IPTC_LIBS | sed -e "s/ *-L[^ ]* */ /" -e "s/ *-liptc */ /" -e "s/^ *$//"` if test ".$IPTC_LIBS" = .; then KA_LIBS="$KA_LIBS -lip4tc -lip6tc" fi else if test $PKG_CONFIG_IP4TC = Yes then : if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libip4tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libip4tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libip6tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libip6tc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" else $as_nop if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libiptc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libiptc`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" fi CONFIG_OPTIONS="$CONFIG_OPTIONS LIBIPTC_DYNAMIC" printf "%s\n" "#define _LIBIPTC_DYNAMIC_ 1 " >>confdefs.h NEED_LIBDL=Yes { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing ip6tc_init" >&5 printf %s "checking for library containing ip6tc_init... " >&6; } if test ${ac_cv_search_ip6tc_init+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char ip6tc_init (); int main (void) { return ip6tc_init (); ; return 0; } _ACEOF for ac_lib in '' $IPTC_LIB_NAMES do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_ip6tc_init=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_ip6tc_init+y} then : break fi done if test ${ac_cv_search_ip6tc_init+y} then : else $as_nop ac_cv_search_ip6tc_init=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ip6tc_init" >&5 printf "%s\n" "$ac_cv_search_ip6tc_init" >&6; } ac_res=$ac_cv_search_ip6tc_init if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi IP4TC_NAME=`echo $ac_cv_search_iptc_init | sed -e "s/-l//"` IP6TC_NAME=`echo $ac_cv_search_ip6tc_init | sed -e "s/-l//"` if test $LDD = :; then as_fn_error $? "ldd is required for dynamic run-time linking support" "$LINENO" 5 fi SAV_LIBS="$LIBS" LIBS="$LIBS -l$IP4TC_NAME" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern void iptc_init(void); int main(void) { iptc_init(); return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : LIB_DETAILS=`$LDD ./conftest$EXEEXT | grep $IP4TC_NAME.so | sed -e "s/^[ \t]*//"` LIB_NAME=`echo $LIB_DETAILS | sed -e "s/ .*//"` fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS="$SAV_LIBS" printf "%s\n" "#define IP4TC_LIB_NAME \"$LIB_NAME\" " >>confdefs.h if test $LDD = :; then as_fn_error $? "ldd is required for dynamic run-time linking support" "$LINENO" 5 fi SAV_LIBS="$LIBS" LIBS="$LIBS -l$IP6TC_NAME" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern void ip6tc_init(void); int main(void) { ip6tc_init(); return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : LIB_DETAILS=`$LDD ./conftest$EXEEXT | grep $IP6TC_NAME.so | sed -e "s/^[ \t]*//"` LIB_NAME=`echo $LIB_DETAILS | sed -e "s/ .*//"` fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS="$SAV_LIBS" printf "%s\n" "#define IP6TC_LIB_NAME \"$LIB_NAME\" " >>confdefs.h LIBIPTC_DYNAMIC=Yes fi else $as_nop USE_IPTABLES=No fi CPPFLAGS="$SAV_CPPFLAGS" fi if test $USE_IPTABLES = Yes; then SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" if test "${enable_libipset}" != no; then $PKG_CONFIG --exists libipset if test $? -eq 0; then if test -n "IPSET"; then KA_PKG_PFX=IPSET else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libipset`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libipset`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test .remove-requires = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libipset` var=`$PKG_CONFIG --libs-only-l libipset` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libipset`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi else IPSET_LIBS="-lipset" IPSET_LIB_NAMES=ipset fi SAV_LIBS=$LIBS { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing ipset_session_init" >&5 printf %s "checking for library containing ipset_session_init... " >&6; } if test ${ac_cv_search_ipset_session_init+y} then : printf %s "(cached) " >&6 else $as_nop ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char ipset_session_init (); int main (void) { return ipset_session_init (); ; return 0; } _ACEOF for ac_lib in '' $IPSET_LIB_NAMES do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO" then : ac_cv_search_ipset_session_init=$ac_res fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext if test ${ac_cv_search_ipset_session_init+y} then : break fi done if test ${ac_cv_search_ipset_session_init+y} then : else $as_nop ac_cv_search_ipset_session_init=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ipset_session_init" >&5 printf "%s\n" "$ac_cv_search_ipset_session_init" >&6; } ac_res=$ac_cv_search_ipset_session_init if test "$ac_res" != no then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" USE_LIBIPSET=Yes for ac_header in libipset/data.h libipset/linux_ip_set.h libipset/session.h libipset/types.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop USE_LIBIPSET=No break fi done if test $USE_LIBIPSET = Yes; then printf "%s\n" "#define _HAVE_LIBIPSET_ 1 " >>confdefs.h $PKG_CONFIG --exists libipset if test $? -eq 0; then if test .${enable_libipset_dynamic} = .no; then if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libipset`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libipset`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libipset` var=`$PKG_CONFIG --libs-only-l libipset` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libipset`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi else if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libipset`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libipset`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" fi elif test .${enable_libipset_dynamic} = .no; then KA_LIBS="$KA_LIBS $ac_cv_search_ipset_session_init" fi if test .${enable_libipset_dynamic} != .no; then printf "%s\n" "#define _LIBIPSET_DYNAMIC_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS LIBIPSET_DYNAMIC" NEED_LIBDL=Yes LIBIPSET_NAME=`echo $ac_cv_search_ipset_session_init | sed -e "s/-l//"` if test $LDD = :; then as_fn_error $? "ldd is required for dynamic run-time linking support" "$LINENO" 5 fi SAV_LIBS="$LIBS" LIBS="$LIBS -l$LIBIPSET_NAME" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern void ipset_session_init(void); int main(void) { ipset_session_init(); return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : LIB_DETAILS=`$LDD ./conftest$EXEEXT | grep $LIBIPSET_NAME.so | sed -e "s/^[ \t]*//"` LIB_NAME=`echo $LIB_DETAILS | sed -e "s/ .*//"` fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS="$SAV_LIBS" printf "%s\n" "#define IPSET_LIB_NAME \"$LIB_NAME\" " >>confdefs.h else CONFIG_OPTIONS="$CONFIG_OPTIONS LIBIPSET" fi ac_fn_c_check_member "$LINENO" "struct xt_set_info_match_v4" "match_set.index" "ac_cv_member_struct_xt_set_info_match_v4_match_set_index" "#include " if test "x$ac_cv_member_struct_xt_set_info_match_v4_match_set_index" = xyes then : printf "%s\n" "#define HAVE_XT_SET_INFO_MATCH_V4 1 " >>confdefs.h fi fi if test $USE_LIBIPSET = Yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libipset version 7 or later" >&5 printf %s "checking for libipset version 7 or later... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include void test_func(void) { ipset_session_init(NULL, NULL); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } printf "%s\n" "#define LIBIPSET_PRE_V7_COMPAT 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS "LIBIPSET_PRE_V7 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi LIBS="$SAV_LIBS" fi CPPFLAGS="$SAV_CPPFLAGS" fi if test $USE_IPTABLES = Yes; then printf "%s\n" "#define _WITH_IPTABLES_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPTABLES" fi fi if test $USE_LIBIPSET = Yes; then LIBIPSET_TRUE= LIBIPSET_FALSE='#' else LIBIPSET_TRUE='#' LIBIPSET_FALSE= fi if test $USE_IPTABLES = Yes; then IPTABLES_TRUE= IPTABLES_FALSE='#' else IPTABLES_TRUE='#' IPTABLES_FALSE= fi if test $USE_IPTABLES = Yes -a .$LIBIPTC_DYNAMIC = .Yes; then LIBIPTC_DYNAMIC_TRUE= LIBIPTC_DYNAMIC_FALSE='#' else LIBIPTC_DYNAMIC_TRUE='#' LIBIPTC_DYNAMIC_FALSE= fi if test $USE_LIBIPSET = Yes -a .${enable_libipset_dynamic} != .no; then LIBIPSET_DYNAMIC_TRUE= LIBIPSET_DYNAMIC_FALSE='#' else LIBIPSET_DYNAMIC_TRUE='#' LIBIPSET_DYNAMIC_FALSE= fi unset LIBS USE_NFTABLES=No if test .${enable_nftables} != .no; then USE_NFTABLES=Yes SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" ac_fn_check_decl "$LINENO" "NFTA_TABLE_MAX" "ac_cv_have_decl_NFTA_TABLE_MAX" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFTA_TABLE_MAX" = xyes then : else $as_nop if test .${enable_nftables} = .yes then : as_fn_error $? "nftables header files missing/not useable" "$LINENO" 5 fi USE_NFTABLES=No fi if test $USE_NFTABLES = Yes; then $PKG_CONFIG --exists libnftnl if test $? -ne 0; then USE_NFTABLES=No { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: libnftnl missing" >&5 printf "%s\n" "$as_me: WARNING: libnftnl missing" >&2;} fi $PKG_CONFIG --exists libmnl if test $? -ne 0; then USE_NFTABLES=No { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: libmnl missing" >&5 printf "%s\n" "$as_me: WARNING: libmnl missing" >&2;} fi if test $USE_NFTABLES = Yes; then # nft prior to version 0.8.3 does not support type ifname in sets. We can't check the version of # nft, but we can check the version of libnftnl. nft v0.8.3 required libnftnl v1.0.9, but so did # nft v0.8.2. So play safe, and require the next version. LIBNFTNL_VERSION=`printf "0x%2.2x%2.2x%2.2xU" \`$PKG_CONFIG --modversion libnftnl | sed -e "s/\./ /g"\`` printf "%s\n" "#define LIBNFTNL_VERSION $LIBNFTNL_VERSION " >>confdefs.h if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnftnl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnftnl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libnftnl` var=`$PKG_CONFIG --libs-only-l libnftnl` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libnftnl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libmnl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libmnl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libmnl` var=`$PKG_CONFIG --libs-only-l libmnl` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libmnl`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi printf "%s\n" "#define _WITH_NFTABLES_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS NFTABLES" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether NFTNL_EXPR_LOOKUP_FLAGS and NFT_LOOKUP_F_INV are defined" >&5 printf %s "checking whether NFTNL_EXPR_LOOKUP_FLAGS and NFT_LOOKUP_F_INV are defined... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include // libnftnl/expr.h requires this #include #include int main(void) { int i = NFTNL_EXPR_LOOKUP_FLAGS | NFT_LOOKUP_F_INV; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_NFTNL_EXPR_LOOKUP_FLAG_INV 1 " >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # nft l4proto from Linux 3.14 ac_fn_check_decl "$LINENO" "NFT_META_L4PROTO" "ac_cv_have_decl_NFT_META_L4PROTO" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFT_META_L4PROTO" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFT_META_L4PROTO $ac_have_decl" >>confdefs.h # nft dup from Linux 4.3 ac_fn_check_decl "$LINENO" "NFTA_DUP_MAX" "ac_cv_have_decl_NFTA_DUP_MAX" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFTA_DUP_MAX" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFTA_DUP_MAX $ac_have_decl" >>confdefs.h # nft meta oifkind from Linux 5.1 ac_fn_check_decl "$LINENO" "NFT_META_OIFKIND" "ac_cv_have_decl_NFT_META_OIFKIND" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFT_META_OIFKIND" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFT_META_OIFKIND $ac_have_decl" >>confdefs.h # NFT_USERDATA_MAXLEN since Linux 3.15. Check nftnl_udata_buf_alloc for libnftnl support of userdata USE_NFT_USERDATA=Yes ac_fn_check_decl "$LINENO" "NFT_USERDATA_MAXLEN" "ac_cv_have_decl_NFT_USERDATA_MAXLEN" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFT_USERDATA_MAXLEN" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFT_USERDATA_MAXLEN $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop USE_NFT_USERDATA=No fi ac_fn_check_decl "$LINENO" "nftnl_udata_buf_alloc" "ac_cv_have_decl_nftnl_udata_buf_alloc" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_nftnl_udata_buf_alloc" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFTNL_UDATA_BUF_ALLOC $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop USE_NFT_USERDATA=No fi if test $USE_NFT_USERDATA = Yes then : printf "%s\n" "#define HAVE_NFTNL_UDATA 1 " >>confdefs.h ac_fn_check_decl "$LINENO" "nftnl_udata_put_u32" "ac_cv_have_decl_nftnl_udata_put_u32" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_nftnl_udata_put_u32" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFTNL_UDATA_PUT_U32 $ac_have_decl" >>confdefs.h fi # NFTNL_SET_DESC_CONCAT since Linux 5.6 and libnftnl 1.1.6, nft 0.9.4 - ranges in concatenations # NFTNL_SET_ELEM_KEY_END since Linux 5.6 and libnftnl 1.1.6, nft 0.9.4 # NFT_SET_CONCAT since Linux 5.7 NFT_RANGE_CONCATS_OK=1 ac_fn_check_decl "$LINENO" "NFTNL_SET_DESC_CONCAT" "ac_cv_have_decl_NFTNL_SET_DESC_CONCAT" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFTNL_SET_DESC_CONCAT" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFTNL_SET_DESC_CONCAT $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop NFT_RANGE_CONCATS_OK=0 fi ac_fn_check_decl "$LINENO" "NFTNL_SET_ELEM_KEY_END" "ac_cv_have_decl_NFTNL_SET_ELEM_KEY_END" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFTNL_SET_ELEM_KEY_END" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFTNL_SET_ELEM_KEY_END $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop NFT_RANGE_CONCATS_OK=0 fi ac_fn_check_decl "$LINENO" "NFT_SET_CONCAT" "ac_cv_have_decl_NFT_SET_CONCAT" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFT_SET_CONCAT" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFT_SET_CONCAT $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop NFT_RANGE_CONCATS_OK=0 fi if test $NFT_RANGE_CONCATS_OK -eq 1 then : printf "%s\n" "#define NFT_RANGE_CONCATS 1 " >>confdefs.h fi # NFTNL_SET_EXPR since Linux 5.7, libnftnl 1.1.7, nft 0.9.5 - counters on set elements ac_fn_check_decl "$LINENO" "NFTNL_SET_EXPR" "ac_cv_have_decl_NFTNL_SET_EXPR" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_NFTNL_SET_EXPR" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_NFTNL_SET_EXPR $ac_have_decl" >>confdefs.h fi fi CPPFLAGS="$SAV_CPPFLAGS" fi if test $USE_NFTABLES = Yes; then NFTABLES_TRUE= NFTABLES_FALSE='#' else NFTABLES_TRUE='#' NFTABLES_FALSE= fi unset LIBS if test $USE_IPTABLES = Yes -o $USE_NFTABLES = Yes then : printf "%s\n" "#define _WITH_FIREWALL_ 1 " >>confdefs.h fi if test $USE_IPTABLES = Yes -o $USE_NFTABLES = Yes; then FIREWALL_TRUE= FIREWALL_FALSE='#' else FIREWALL_TRUE='#' FIREWALL_FALSE= fi # Including and can cause a namespace collision. # Later versions of the headers are OK if linux/if.h is included second { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linux/if.h and net/if.h namespace collision" >&5 printf %s "checking for linux/if.h and net/if.h namespace collision... " >&6; } SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_NET_LINUX_IF_H_COLLISION_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS NET_LINUX_IF_H_COLLISION" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CPPFLAGS="$SAV_CPPFLAGS" # This issue was resolved in Linux 4.15.7/4.16 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linux/if_ether.h then netinet/in.h then linux/if.h namespace collision" >&5 printf %s "checking for linux/if_ether.h then netinet/in.h then linux/if.h namespace collision... " >&6; } SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_LINUX_IF_ETHER_H_COLLISION_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS NET_LINUX_IF_ETHER_H_COLLISION" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CPPFLAGS="$SAV_CPPFLAGS" # Including and causes a namespace collision # with musl libc, but the collision only occurs if linux/ip_ether.h is included # before netinet/if_ether.h. The problem is that we want to include them in that # order. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linux/if_ether.h then netinet/if_ether.h namespace collision" >&5 printf %s "checking for linux/if_ether.h then netinet/if_ether.h namespace collision... " >&6; } SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_NETINET_LINUX_IF_ETHER_H_COLLISION_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS NETINET_LINUX_IF_ETHER_H_COLLISION" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CPPFLAGS="$SAV_CPPFLAGS" # Linux 4.5 to 4.5.4 has indirectly including # and which causes a namespace collision. { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libiptc/libiptc.h linux/if.h and net/if.h namespace collision" >&5 printf %s "checking for libiptc/libiptc.h linux/if.h and net/if.h namespace collision... " >&6; } SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define _HAVE_LIBIPTC_LINUX_NET_IF_H_COLLISION_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS LIBIPTC_LINUX_NET_IF_H_COLLISION" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CPPFLAGS="$SAV_CPPFLAGS" IPVS_SYNCD_ATTRIBUTES=No IPVS_64BIT_STATS=No WITH_REGEX=No ENABLE_REGEX_DEBUG=No if test "$enable_lvs" != no; then IPVS_SUPPORT=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS LVS" printf "%s\n" "#define _WITH_LVS_ 1 " >>confdefs.h if test $IPVS_USE_NL = Yes; then printf "%s\n" "#define LIBIPVS_USE_NL 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS LIBIPVS_NETLINK" fi SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" ac_fn_check_decl "$LINENO" "IPVS_DEST_ATTR_ADDR_FAMILY" "ac_cv_have_decl_IPVS_DEST_ATTR_ADDR_FAMILY" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_DEST_ATTR_ADDR_FAMILY" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPVS_DEST_ATTR_ADDR_FAMILY" fi IPVS_SYNCD_ATTRIBUTES=Yes ac_fn_check_decl "$LINENO" "IPVS_DAEMON_ATTR_SYNC_MAXLEN" "ac_cv_have_decl_IPVS_DAEMON_ATTR_SYNC_MAXLEN" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_DAEMON_ATTR_SYNC_MAXLEN" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_DAEMON_ATTR_SYNC_MAXLEN $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPVS_SYNCD_ATTRIBUTES=No break fi ac_fn_check_decl "$LINENO" "IPVS_DAEMON_ATTR_MCAST_GROUP" "ac_cv_have_decl_IPVS_DAEMON_ATTR_MCAST_GROUP" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_DAEMON_ATTR_MCAST_GROUP" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_DAEMON_ATTR_MCAST_GROUP $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPVS_SYNCD_ATTRIBUTES=No break fi ac_fn_check_decl "$LINENO" "IPVS_DAEMON_ATTR_MCAST_GROUP6" "ac_cv_have_decl_IPVS_DAEMON_ATTR_MCAST_GROUP6" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_DAEMON_ATTR_MCAST_GROUP6" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_DAEMON_ATTR_MCAST_GROUP6 $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPVS_SYNCD_ATTRIBUTES=No break fi ac_fn_check_decl "$LINENO" "IPVS_DAEMON_ATTR_MCAST_PORT" "ac_cv_have_decl_IPVS_DAEMON_ATTR_MCAST_PORT" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_DAEMON_ATTR_MCAST_PORT" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_DAEMON_ATTR_MCAST_PORT $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPVS_SYNCD_ATTRIBUTES=No break fi ac_fn_check_decl "$LINENO" "IPVS_DAEMON_ATTR_MCAST_TTL" "ac_cv_have_decl_IPVS_DAEMON_ATTR_MCAST_TTL" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_DAEMON_ATTR_MCAST_TTL" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_DAEMON_ATTR_MCAST_TTL $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPVS_SYNCD_ATTRIBUTES=No break fi if test $IPVS_SYNCD_ATTRIBUTES = Yes; then printf "%s\n" "#define _HAVE_IPVS_SYNCD_ATTRIBUTES_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPVS_SYNCD_ATTRIBUTES" fi if test "$enable_lvs_64bit_stats" != "no"; then IPVS_64BIT_STATS=Yes ac_fn_check_decl "$LINENO" "IPVS_SVC_ATTR_STATS64" "ac_cv_have_decl_IPVS_SVC_ATTR_STATS64" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_SVC_ATTR_STATS64" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_SVC_ATTR_STATS64 $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPVS_64BIT_STATS=No break fi ac_fn_check_decl "$LINENO" "IPVS_DEST_ATTR_STATS64" "ac_cv_have_decl_IPVS_DEST_ATTR_STATS64" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_DEST_ATTR_STATS64" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_DEST_ATTR_STATS64 $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPVS_64BIT_STATS=No break fi if test $IPVS_64BIT_STATS = Yes; then printf "%s\n" "#define _WITH_LVS_64BIT_STATS_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPVS_64BIT_STATS" fi fi ac_fn_check_decl "$LINENO" "IPVS_DEST_ATTR_TUN_TYPE" "ac_cv_have_decl_IPVS_DEST_ATTR_TUN_TYPE" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVS_DEST_ATTR_TUN_TYPE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVS_DEST_ATTR_TUN_TYPE $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : printf "%s\n" "#define _HAVE_IPVS_TUN_TYPE_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPVS_TUN_TYPE" fi ac_fn_check_decl "$LINENO" "IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM" "ac_cv_have_decl_IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : printf "%s\n" "#define _HAVE_IPVS_TUN_CSUM_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPVS_TUN_CSUM" fi ac_fn_check_decl "$LINENO" "IP_VS_CONN_F_TUNNEL_TYPE_GRE" "ac_cv_have_decl_IP_VS_CONN_F_TUNNEL_TYPE_GRE" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IP_VS_CONN_F_TUNNEL_TYPE_GRE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IP_VS_CONN_F_TUNNEL_TYPE_GRE $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : printf "%s\n" "#define _HAVE_IPVS_TUN_GRE_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS IPVS_TUN_GRE" fi CPPFLAGS="$SAV_CPPFLAGS" if test "$enable_regex" = yes then : $PKG_CONFIG --exists libpcre2-8 HAVE_PCRE2=$? if test $HAVE_PCRE2 -ne 0 then : as_fn_error $? "cannot find 8-bit pcre library" "$LINENO" 5 fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pcre.h" >&5 printf %s "checking for pcre.h... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define PCRE2_CODE_UNIT_WIDTH 8 #include _ACEOF if ac_fn_c_try_compile "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } WITH_REGEX=Yes if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libpcre2-8`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libpcre2-8`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libpcre2-8` var=`$PKG_CONFIG --libs-only-l libpcre2-8` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libpcre2-8`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi printf "%s\n" "#define _WITH_REGEX_CHECK_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS REGEX" else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "pcre2.h is missing" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test "$enable_regex_timers" = yes; then printf "%s\n" "#define _WITH_REGEX_TIMERS_ 1 " >>confdefs.h fi if test "${enable_regex_debug}" = yes; then printf "%s\n" "#define _REGEX_DEBUG_ 1 " >>confdefs.h ENABLE_REGEX_DEBUG=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS REGEX_DEBUG" fi fi else IPVS_SUPPORT=No fi if test $IPVS_SUPPORT = Yes; then WITH_IPVS_TRUE= WITH_IPVS_FALSE='#' else WITH_IPVS_TRUE='#' WITH_IPVS_FALSE= fi if test $WITH_REGEX = Yes; then WITH_REGEX_TRUE= WITH_REGEX_FALSE='#' else WITH_REGEX_TRUE='#' WITH_REGEX_FALSE= fi VRRP_SUPPORT=No VRRP_AUTH_SUPPORT=No MACVLAN_SUPPORT=No ENABLE_JSON=No BFD_SUPPORT=No HAVE_CN_PROC=No WITH_TRACK_PROCESS=No if test "$enable_vrrp" != no; then VRRP_SUPPORT=Yes printf "%s\n" "#define _WITH_VRRP_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS VRRP" if test "${enable_vrrp_auth}" != no; then VRRP_AUTH_SUPPORT=Yes printf "%s\n" "#define _WITH_VRRP_AUTH_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS VRRP_AUTH" fi SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" MACVLAN_SUPPORT=No if test "${enable_vmac}" != no; then MACVLAN_SUPPORT=Yes printf "%s\n" "#define _HAVE_VRRP_VMAC_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS VRRP_VMAC" IPVLAN_SUPPORT=Yes ac_fn_check_decl "$LINENO" "IFLA_IPVLAN_MODE" "ac_cv_have_decl_IFLA_IPVLAN_MODE" " #include #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IFLA_IPVLAN_MODE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IFLA_IPVLAN_MODE $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : else $as_nop IPVLAN_SUPPORT=No break fi if test $IPVLAN_SUPPORT = Yes; then ac_fn_check_decl "$LINENO" "IPVLAN_MODE_L3S" "ac_cv_have_decl_IPVLAN_MODE_L3S" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IPVLAN_MODE_L3S" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IPVLAN_MODE_L3S $ac_have_decl" >>confdefs.h ac_fn_check_decl "$LINENO" "IFLA_IPVLAN_FLAGS" "ac_cv_have_decl_IFLA_IPVLAN_FLAGS" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IFLA_IPVLAN_FLAGS" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IFLA_IPVLAN_FLAGS $ac_have_decl" >>confdefs.h printf "%s\n" "#define _HAVE_VRRP_IPVLAN_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS VRRP_IPVLAN" fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { int var = IFLA_LINK_NETNSID; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_IFLA_LINK_NETNSID 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS IFLA_LINK_NETNSID" fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext fi CPPFLAGS="$SAV_CPPFLAGS" if test "${enable_json}" = yes; then ENABLE_JSON=Yes printf "%s\n" "#define _WITH_JSON_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS JSON" fi if test "${enable_bfd}" = yes; then BFD_SUPPORT=Yes printf "%s\n" "#define _WITH_BFD_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS BFD" fi if test .$enable_track_process != .no then : WITH_TRACK_PROCESS=Yes printf "%s\n" "#define _WITH_TRACK_PROCESS_ 1 " >>confdefs.h else $as_nop CONFIG_OPTIONS="$CONFIG_OPTIONS DISABLE_TRACK_PROCESS" fi fi if test $VRRP_SUPPORT = Yes; then WITH_VRRP_TRUE= WITH_VRRP_FALSE='#' else WITH_VRRP_TRUE='#' WITH_VRRP_FALSE= fi if test $VRRP_AUTH_SUPPORT = Yes; then VRRP_AUTH_TRUE= VRRP_AUTH_FALSE='#' else VRRP_AUTH_TRUE='#' VRRP_AUTH_FALSE= fi if test $MACVLAN_SUPPORT = Yes; then VMAC_TRUE= VMAC_FALSE='#' else VMAC_TRUE='#' VMAC_FALSE= fi if test $ENABLE_JSON = Yes; then WITH_JSON_TRUE= WITH_JSON_FALSE='#' else WITH_JSON_TRUE='#' WITH_JSON_FALSE= fi if test $BFD_SUPPORT = Yes; then WITH_BFD_TRUE= WITH_BFD_FALSE='#' else WITH_BFD_TRUE='#' WITH_BFD_FALSE= fi if test $WITH_TRACK_PROCESS = Yes; then TRACK_PROCESS_TRUE= TRACK_PROCESS_FALSE='#' else TRACK_PROCESS_TRUE='#' TRACK_PROCESS_FALSE= fi if test ${IPVS_SUPPORT} = No -a ${VRRP_SUPPORT} = No; then as_fn_error $? "keepalived MUST be compiled with at least one of LVS or VRRP framework" "$LINENO" 5 fi ac_fn_check_decl "$LINENO" "GLOB_BRACE" "ac_cv_have_decl_GLOB_BRACE" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_GLOB_BRACE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_GLOB_BRACE $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : SYSTEM_OPTIONS="$SYSTEM_OPTIONS GLOB_BRACE" fi ac_fn_check_decl "$LINENO" "GLOB_ALTDIRFUNC" "ac_cv_have_decl_GLOB_ALTDIRFUNC" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_GLOB_ALTDIRFUNC" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_GLOB_ALTDIRFUNC $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : SYSTEM_OPTIONS="$SYSTEM_OPTIONS GLOB_ALTDIRFUNC" fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for timegm()" >&5 printf %s "checking for timegm()... " >&6; } SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$KA_CPPFLAGS $kernel_inc" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { timegm(NULL); return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } printf "%s\n" "#define HAVE_TIMEGM 1 " >>confdefs.h else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext CPPFLAGS="$SAV_CPPFLAGS" UNICAST_CHKSUM_COMPAT_SUPPORT=No if test .$enable_checksum_compat != .no; then UNICAST_CHKSUM_COMPAT_SUPPORT=Yes printf "%s\n" "#define _WITH_UNICAST_CHKSUM_COMPAT_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS OLD_CHKSUM_COMPAT" fi if test .$enable_linkbeat = .no then : LINKBEAT_SUPPORT=No CONFIG_OPTIONS="$CONFIG_OPTIONS NO_LINKBEAT" else $as_nop LINKBEAT_SUPPORT=Yes printf "%s\n" "#define _WITH_LINKBEAT_ 1 " >>confdefs.h fi if test .$enable_sockaddr_storage = .yes then : printf "%s\n" "#define USE_SOCKADDR_STORAGE 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS SOCKADDR_STORAGE" fi SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" if test ${MACVLAN_SUPPORT} = Yes; then # Introduced in Linux 3.17 ac_fn_check_decl "$LINENO" "IFLA_INET6_ADDR_GEN_MODE" "ac_cv_have_decl_IFLA_INET6_ADDR_GEN_MODE" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IFLA_INET6_ADDR_GEN_MODE" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : SYSTEM_OPTIONS="$SYSTEM_OPTIONS INET6_ADDR_GEN_MODE" fi fi CPPFLAGS="$SAV_CPPFLAGS" if test ${MACVLAN_SUPPORT} = Yes; then SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" # Introduced in Linux 4.3 ac_fn_check_decl "$LINENO" "IFLA_VRF_MAX" "ac_cv_have_decl_IFLA_VRF_MAX" " #include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_IFLA_VRF_MAX" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_IFLA_VRF_MAX $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : SYSTEM_OPTIONS="$SYSTEM_OPTIONS VRF" printf "%s\n" "#define _HAVE_VRF_ 1 " >>confdefs.h fi CPPFLAGS="$SAV_CPPFLAGS" fi LIBNM_SUPPORT=No if test \( ${MACVLAN_SUPPORT} = Yes -a .$enable_nm = .yes \) then : $PKG_CONFIG --exists libnm if test $? -eq 0 then : printf "%s\n" "#define _HAVE_LIBNM_ 1 " >>confdefs.h if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libnm`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libnm`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libnm` var=`$PKG_CONFIG --libs-only-l libnm` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libnm`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi SYSTEM_OPTIONS="$SYSTEM_OPTIONS LIBNM" LIBNM_SUPPORT=Yes else $as_nop as_fn_error $? "--enable-nm requires NetworkManager libnm development package to be installed" "$LINENO" 5 fi fi if test $LIBNM_SUPPORT = Yes; then NETWORK_MANAGER_TRUE= NETWORK_MANAGER_FALSE='#' else NETWORK_MANAGER_TRUE='#' NETWORK_MANAGER_FALSE= fi SNMP_SUPPORT=No SNMP_KEEPALIVED_SUPPORT=No SNMP_VRRP_SUPPORT=No SNMP_RFC_SUPPORT=No SNMP_RFCV2_SUPPORT=No SNMP_RFCV3_SUPPORT=No SNMP_CHECKER_SUPPORT=No SNMP_V3_FOR_V2=No if test "$enable_snmp_keepalived" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: --enable-snmp-keepalived is obsolete. Use --enable-snmp-vrrp." >&5 printf "%s\n" "$as_me: WARNING: --enable-snmp-keepalived is obsolete. Use --enable-snmp-vrrp." >&2;} enable_snmp_vrrp=$enable_snmp_keepalived fi if test "$enable_snmp" = yes -o \ "$enable_snmp_vrrp" = yes -o \ "$enable_snmp_checker" = yes -o \ "$enable_snmp_rfc" = yes -o \ "$enable_snmp_rfcv2" = yes -o \ "$enable_snmp_rfcv3" = yes; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}net-snmp-config", so it can be a program name with args. set dummy ${ac_tool_prefix}net-snmp-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_NETSNMP_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $NETSNMP_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_NETSNMP_CONFIG="$NETSNMP_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_NETSNMP_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi NETSNMP_CONFIG=$ac_cv_path_NETSNMP_CONFIG if test -n "$NETSNMP_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $NETSNMP_CONFIG" >&5 printf "%s\n" "$NETSNMP_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi fi if test -z "$ac_cv_path_NETSNMP_CONFIG"; then ac_pt_NETSNMP_CONFIG=$NETSNMP_CONFIG # Extract the first word of "net-snmp-config", so it can be a program name with args. set dummy net-snmp-config; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_path_ac_pt_NETSNMP_CONFIG+y} then : printf %s "(cached) " >&6 else $as_nop case $ac_pt_NETSNMP_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_NETSNMP_CONFIG="$ac_pt_NETSNMP_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_NETSNMP_CONFIG="$as_dir$ac_word$ac_exec_ext" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_NETSNMP_CONFIG=$ac_cv_path_ac_pt_NETSNMP_CONFIG if test -n "$ac_pt_NETSNMP_CONFIG"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_pt_NETSNMP_CONFIG" >&5 printf "%s\n" "$ac_pt_NETSNMP_CONFIG" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test "x$ac_pt_NETSNMP_CONFIG" = x; then NETSNMP_CONFIG="no" else case $cross_compiling:$ac_tool_warned in yes:) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NETSNMP_CONFIG=$ac_pt_NETSNMP_CONFIG fi else NETSNMP_CONFIG="$ac_cv_path_NETSNMP_CONFIG" fi if test "$NETSNMP_CONFIG" = no; then as_fn_error $? "*** unable to find net-snmp-config" "$LINENO" 5 fi # Despite the net-snmp-config documentation for the --*-libs and --base-cflags # options documention suggesting they provide the libraries and -I options # respectively, they actually include all the other # LDFLAGS and CFLAGS options # used when net-snmp was built. Since net-snmp is likely to have been built using # the distro's package builder, this can include quite a large number of # compiler/linker flags that we don't really want. # RedHat has a patch that stops the additional compile options being emitted with # --cflags and --base-cflags, however it doesn't have a patch to do similarly for # the --*-libs options. This means that the --cflags options do not include the # -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1, whereas the --*-libs options # do include -specs=/usr/lib/rpm/redhat/redhat-hardened-ld. The result of this is # -pie is specified for the linker, but -fpie is not specified for the compiler. # It turns out that this does not cause a problem on x86_64, but building on s390x # fails to link due to this mismatch. # See https://bugzilla.redhat.com/show_bug.cgi?id=1921988 for further details. NETSNMP_LIBS_AGENT=`${NETSNMP_CONFIG} --netsnmp-agent-libs` NETSNMP_LIBS_EXT=`${NETSNMP_CONFIG} --external-libs` NETSNMP_CFLAGS=`${NETSNMP_CONFIG} --base-cflags` NETSNMP_CPPFLAGS="-DNETSNMP_NO_INLINE" echo $NETSNMP_LIBS_EXT | $GREP -q -- -specs= if test $? -eq 0 then : HAVE_LD_SPECS=1 else $as_nop HAVE_LD_SPECS=0 fi echo $NETSNMP_CFLAGS | $GREP -q -- -specs= if test $? -eq 0 then : HAVE_CC_SPECS=1 else $as_nop HAVE_CC_SPECS=0 fi if test \( $HAVE_LD_SPECS -eq 1 -a $HAVE_CC_SPECS -eq 0 \) then : NETSNMP_LIBS_EXT=`echo $NETSNMP_LIBS_EXT | tr " " "\n" | grep "^-lL"` fi NETSNMP_LIBS="$NETSNMP_LIBS_AGENT $NETSNMP_LIBS_EXT" # net-snmp-config can add -I/usr/include, so remove it NETSNMP_CFLAGS=`echo $NETSNMP_CFLAGS " " | sed -e "s:-I */usr/include ::"` # net-snmp-config --base-cflags can specify include directories that don't # exist on the build system NEW_FLAGS= for f in `echo $NETSNMP_CFLAGS | tr " " "\n" | grep "^-I"`; do DIR=`echo $f | sed -e "s/^-I *//"` if test -d $DIR then : NEW_FLAGS+=" -I$DIR" fi done NETSNMP_CFLAGS=$NEW_FLAGS # net-snmp-config can specify LTO flags; remove them so they are only # included if --enable-lto is specified NETSNMP_CFLAGS=`echo " $NETSNMP_CFLAGS " | sed -e "s/-f[^ ]*lto[^ ]* //g"` # net-snmp-config adds compiler and linker options that were set at the time # net-snmp was built, and this can include spec files that may not exist # on the system building keepalived. We need to check if any spec files # are specified, and if they do not exist on this system, then remove them # from NETSNMP_LIBS or NETSNMP_CFLAGS. # For further information, see https://bugzilla.redhat.com/show_bug.cgi?id=1544527 # and the other bugs referred to in it. for spec in `echo $NETSNMP_LIBS | sed -e "s? ?\n?g" | grep "^-specs="`; do SPEC_FILE=`echo $spec | sed -e "s?^-specs=??"` if test ! -f $SPEC_FILE; then NETSNMP_LIBS=`echo $NETSNMP_LIBS | sed -e "s? *$spec *? ?"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Removing $spec from NETSNMP_LIBS since spec file not installed" >&5 printf "%s\n" "$as_me: WARNING: Removing $spec from NETSNMP_LIBS since spec file not installed" >&2;} fi done for spec in `echo $NETSNMP_CFLAGS | sed -e "s? ?\n?g" | grep "^-specs="`; do SPEC_FILE=`echo $spec | sed -e "s?^-specs=??"` if test ! -f $SPEC_FILE; then NETSNMP_CFLAGS=`echo $NETSNMP_CFLAGS | sed -e "s? *$spec *? ?"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Removing $spec from NETSNMP_CFLAGS since spec file not installed" >&5 printf "%s\n" "$as_me: WARNING: Removing $spec from NETSNMP_CFLAGS since spec file not installed" >&2;} fi done SAV_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS ${NETSNMP_CFLAGS}" SAV_LIBS="$LIBS" LIBS="$LIBS ${NETSNMP_LIBS}" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler supports flag \"${NETSNMP_CFLAGS} ${NETSNMP_LIBS}\" from Net-SNMP" >&5 printf %s "checking whether C compiler supports flag \"${NETSNMP_CFLAGS} ${NETSNMP_LIBS}\" from Net-SNMP... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main(void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } else $as_nop { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } as_fn_error $? "*** incorrect CFLAGS from net-snmp-config" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext # Do we have subagent support? for ac_func in netsnmp_enable_subagent do : ac_fn_c_check_func "$LINENO" "netsnmp_enable_subagent" "ac_cv_func_netsnmp_enable_subagent" if test "x$ac_cv_func_netsnmp_enable_subagent" = xyes then : printf "%s\n" "#define HAVE_NETSNMP_ENABLE_SUBAGENT 1" >>confdefs.h else $as_nop as_fn_error $? "*** no subagent support in net-snmp" "$LINENO" 5 fi done # check for net-snmp headers # Some ancient distributions may miss header SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernel_inc $NETSNMP_CFLAGS" for ac_header in net-snmp/agent/agent_sysORTable.h net-snmp/agent/snmp_vars.h net-snmp/agent/util_funcs.h do : as_ac_Header=`printf "%s\n" "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" " #include #include #include " if eval test \"x\$"$as_ac_Header"\" = x"yes" then : cat >>confdefs.h <<_ACEOF #define `printf "%s\n" "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF else $as_nop as_fn_error $? "missing net-snmp headers" "$LINENO" 5 fi done SNMP_SUPPORT=Yes # NETSNMP_CFLAGS can have CPPFLAGS options, so separate them NETSNMP_CPPFLAGS_XTRA=`echo " $NETSNMP_CFLAGS " | sed -e "s/ / /g" -e "s/ -[^IDU] *-/ -/g" -e "s/ -[^IDU] *[^-][^ ]* / /g" -e "s/ */ /g"` NETSNMP_CFLAGS=`echo " $NETSNMP_CFLAGS " | sed -e "s/ / /g" -e "s/ -[IDU] *[^ ]* / /g" -e "s/ */ /g"` KA_CFLAGS="$KA_CFLAGS $NETSNMP_CFLAGS" KA_CPPFLAGS="$KA_CPPFLAGS $NETSNMP_CPPFLAGS $NETSNMP_CPPFLAGS_XTRA" # NETSNMP_LIBS may have some LDFLAGS options, so separate them NETSNMP_LDFLAGS_XTRA=`echo " $NETSNMP_LIBS " | sed -e "s/ / /g" -e "s/ -l *[^ ]* / /g" -e "s/ */ /g" -e "s/ -/ @-/g" | tr "@" "\n" | sed -e "s/^ *//" -e "s/ *$//" | sort -u | tr "\n" " "` NETSNMP_LIBS=`echo " $NETSNMP_LIBS " | sed -e "s/ / /g" -e "s/ \(-l *[^ ]*\) /@\1@/g" | tr "@" "\n" | grep "^-l" | tr "\n" " " | sed -e "s/ */ /g"` KA_LDFLAGS="$KA_LDFLAGS $NETSNMP_LDFLAGS $NETSNMP_LDFLAGS_XTRA" KA_LIBS="$KA_LIBS $NETSNMP_LIBS" if test "$enable_snmp_rfc" = yes; then SNMP_RFCV2_SUPPORT=Yes SNMP_RFCV3_SUPPORT=Yes else if test "$enable_snmp_rfcv2" = yes; then SNMP_RFCV2_SUPPORT=Yes fi if test "$enable_snmp_rfcv3" = yes; then SNMP_RFCV3_SUPPORT=Yes fi fi if test ${SNMP_RFCV2_SUPPORT} = Yes -o \ ${SNMP_RFCV3_SUPPORT} = Yes; then if test ${VRRP_SUPPORT} != Yes; then as_fn_error $? "RFC SNMP support requires VRRP" "$LINENO" 5 fi SNMP_RFC_SUPPORT=Yes fi if test ${SNMP_RFCV3_SUPPORT} = Yes -a \ "$enable_snmp_reply_v3_for_v2" != no; then printf "%s\n" "#define _SNMP_REPLY_V3_FOR_V2_ 1 " >>confdefs.h SNMP_V3_FOR_V2=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS SNMP_V3_FOR_V2" fi if test "$enable_snmp" = yes; then if test ${VRRP_SUPPORT} = Yes; then SNMP_VRRP_SUPPORT=Yes fi if test ${IPVS_SUPPORT} = Yes; then SNMP_CHECKER_SUPPORT=Yes fi else if test "$enable_snmp_vrrp" = yes; then SNMP_VRRP_SUPPORT=Yes fi if test "$enable_snmp_checker" = yes; then SNMP_CHECKER_SUPPORT=Yes fi fi if test ${VRRP_SUPPORT} != Yes -a \ ${SNMP_VRRP_SUPPORT} = Yes; then as_fn_error $? "VRRP SNMP support requires VRRP" "$LINENO" 5 fi if test ${IPVS_SUPPORT} = No -a \ ${SNMP_CHECKER_SUPPORT} = Yes; then as_fn_error $? "CHECKER SNMP support requires checker" "$LINENO" 5 fi if test ${SNMP_VRRP_SUPPORT} = Yes -o \ ${SNMP_CHECKER_SUPPORT} = Yes; then SNMP_KEEPALIVED_SUPPORT=Yes fi CPPFLAGS="$SAV_CPPFLAGS" CFLAGS="$SAV_CFLAGS" LIBS="$SAV_LIBS" fi if test $SNMP_SUPPORT = Yes; then printf "%s\n" "#define _WITH_SNMP_ 1 " >>confdefs.h fi if test $SNMP_KEEPALIVED_SUPPORT = Yes; then printf "%s\n" "#define _WITH_SNMP_KEEPALIVED_ 1 " >>confdefs.h fi if test $SNMP_VRRP_SUPPORT = Yes; then printf "%s\n" "#define _WITH_SNMP_VRRP_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS SNMP_VRRP" fi if test $SNMP_CHECKER_SUPPORT = Yes; then printf "%s\n" "#define _WITH_SNMP_CHECKER_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS SNMP_CHECKER" fi if test $SNMP_RFC_SUPPORT = Yes; then printf "%s\n" "#define _WITH_SNMP_RFC_ 1 " >>confdefs.h fi if test $SNMP_RFCV2_SUPPORT = Yes; then printf "%s\n" "#define _WITH_SNMP_RFCV2_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS SNMP_RFCV2" fi if test $SNMP_RFCV3_SUPPORT = Yes; then printf "%s\n" "#define _WITH_SNMP_RFCV3_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS SNMP_RFCV3" fi if test $SNMP_SUPPORT = Yes; then SNMP_TRUE= SNMP_FALSE='#' else SNMP_TRUE='#' SNMP_FALSE= fi if test $SNMP_KEEPALIVED_SUPPORT = Yes; then SNMP_KEEPALIVED_TRUE= SNMP_KEEPALIVED_FALSE='#' else SNMP_KEEPALIVED_TRUE='#' SNMP_KEEPALIVED_FALSE= fi if test $SNMP_VRRP_SUPPORT = Yes -o $SNMP_RFC_SUPPORT = Yes; then SNMP_VRRP_TRUE= SNMP_VRRP_FALSE='#' else SNMP_VRRP_TRUE='#' SNMP_VRRP_FALSE= fi if test $SNMP_CHECKER_SUPPORT = Yes; then SNMP_CHECKER_TRUE= SNMP_CHECKER_FALSE='#' else SNMP_CHECKER_TRUE='#' SNMP_CHECKER_FALSE= fi if test $SNMP_RFCV2_SUPPORT = Yes -o $SNMP_RFCV3_SUPPORT = Yes; then SNMP_RFC_TRUE= SNMP_RFC_FALSE='#' else SNMP_RFC_TRUE='#' SNMP_RFC_FALSE= fi if test $SNMP_RFCV2_SUPPORT = Yes; then SNMP_RFCV2_TRUE= SNMP_RFCV2_FALSE='#' else SNMP_RFCV2_TRUE='#' SNMP_RFCV2_FALSE= fi if test $SNMP_RFCV3_SUPPORT = Yes; then SNMP_RFCV3_TRUE= SNMP_RFCV3_FALSE='#' else SNMP_RFCV3_TRUE='#' SNMP_RFCV3_FALSE= fi if test $SNMP_V3_FOR_V2 = Yes; then SNMP_REPLY_V3_FOR_V2_TRUE= SNMP_REPLY_V3_FOR_V2_FALSE='#' else SNMP_REPLY_V3_FOR_V2_TRUE='#' SNMP_REPLY_V3_FOR_V2_FALSE= fi if test $SNMP_SUPPORT = Yes then : SNMP_SERVICE=snmpd.service else $as_nop SNMP_SERVICE= fi DBUS_SUPPORT=No DBUS_CREATE_INSTANCE=No if test "$enable_dbus" = yes; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for g_bus_own_name in -lgio-2.0" >&5 printf %s "checking for g_bus_own_name in -lgio-2.0... " >&6; } if test ${ac_cv_lib_gio_2_0_g_bus_own_name+y} then : printf %s "(cached) " >&6 else $as_nop ac_check_lib_save_LIBS=$LIBS LIBS="-lgio-2.0 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ char g_bus_own_name (); int main (void) { return g_bus_own_name (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO" then : ac_cv_lib_gio_2_0_g_bus_own_name=yes else $as_nop ac_cv_lib_gio_2_0_g_bus_own_name=no fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gio_2_0_g_bus_own_name" >&5 printf "%s\n" "$ac_cv_lib_gio_2_0_g_bus_own_name" >&6; } if test "x$ac_cv_lib_gio_2_0_g_bus_own_name" = xyes then : if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I gio-2.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other gio-2.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires gio-2.0` var=`$PKG_CONFIG --libs-only-l gio-2.0` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs gio-2.0`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi DBUS_SUPPORT=Yes printf "%s\n" "#define _WITH_DBUS_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS DBUS" SAV_CFLAGS=$CFLAGS CFLAGS=`$PKG_CONFIG --cflags gio-2.0` SAV_LIBS=$LIBS LIBS=`$PKG_CONFIG --libs gio-2.0` if test "$cross_compiling" = yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Cannot determine if need to call g_type_init(). Assuming yes for safety." >&5 printf "%s\n" "$as_me: WARNING: Cannot determine if need to call g_type_init(). Assuming yes for safety." >&2;} need_g_type_init=1 else $as_nop cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main (void) { return !g_thread_functions_for_glib_use.mutex_lock; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO" then : need_g_type_init=0 else $as_nop need_g_type_init=1 fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi if test $need_g_type_init -eq 1; then printf "%s\n" "#define DBUS_NEED_G_TYPE_INIT 1 " >>confdefs.h fi LIBS=$SAV_LIBS CFLAGS=$SAV_CFLAGS if test "$enable_dbus_create_instance" = yes; then printf "%s\n" "#define _WITH_DBUS_CREATE_INSTANCE_ 1 " >>confdefs.h DBUS_CREATE_INSTANCE=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS DBUS_CREATE_INSTANCE" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: DBus create instance functionality is dangerous - why do you want it?" >&5 printf "%s\n" "$as_me: WARNING: DBus create instance functionality is dangerous - why do you want it?" >&2;} fi else $as_nop as_fn_error $? "DBUS support requested but libgio-2.0 not found." "$LINENO" 5 fi unset LIBS fi if test $DBUS_SUPPORT = Yes; then WITH_DBUS_TRUE= WITH_DBUS_FALSE='#' else WITH_DBUS_TRUE='#' WITH_DBUS_FALSE= fi if test $DBUS_CREATE_INSTANCE = Yes; then DBUS_CREATE_INSTANCE_TRUE= DBUS_CREATE_INSTANCE_FALSE='#' else DBUS_CREATE_INSTANCE_TRUE='#' DBUS_CREATE_INSTANCE_FALSE= fi SO_MARK_SUPPORT=No if test "${enable_fwmark}" != no; then ac_fn_check_decl "$LINENO" "SO_MARK" "ac_cv_have_decl_SO_MARK" "#include " "$ac_c_undeclared_builtin_options" "CFLAGS" if test "x$ac_cv_have_decl_SO_MARK" = xyes then : ac_have_decl=1 else $as_nop ac_have_decl=0 fi printf "%s\n" "#define HAVE_DECL_SO_MARK $ac_have_decl" >>confdefs.h if test $ac_have_decl = 1 then : SO_MARK_SUPPORT=Yes printf "%s\n" "#define _WITH_SO_MARK_ 1 " >>confdefs.h SYSTEM_OPTIONS="$SYSTEM_OPTIONS SO_MARK" fi fi GNU_STD_PATHS=No if test "${enable_gnu_std_paths}" = "yes"; then printf "%s\n" "#define GNU_STD_PATHS 1 " >>confdefs.h fi if test $TMP_DIR_SPECIFIED = Y; then if test `expr substr ${with_tmp_dir} 1 1` != / then : as_fn_error $? "tmp-dir must be absolute path" "$LINENO" 5 fi # Remove any trailing / - someone will include it sometime KA_TMP_DIR=`echo ${with_tmp_dir} | sed -e "s:/*$::"` else KA_TMP_DIR=/tmp fi printf "%s\n" "#define KA_TMP_DIR \"${KA_TMP_DIR}\" " >>confdefs.h KA_TMP_DIR=${KA_TMP_DIR} if test .${with_iproute_usr_dir} != . then : iproute_usr_dir="$with_iproute_usr_dir" fi if test .${with_iproute_etc_dir} != . then : iproute_etc_dir="$with_iproute_etc_dir" fi if test .${with_iproute_usr_dir} = . && test .${with_iproute_etc_dir} = . then : man ip-route | grep -q /rt_tables if test $? = 0 then : IPROUTE_DIRS=$(man ip route | tr " \t" "\n\n" | $SED -e "s/[\.,;:?\!]*//g" | grep "/rt_tables$" | $SED -e "s:/rt_tables$::") set $(echo $IPROUTE_DIRS | sort -u) second_route=$2 set $IPROUTE_DIRS if test .$second_route = . then : iproute_etc_dir=$1 iproute_usr_dir= else $as_nop iproute_usr_dir=$1 iproute_etc_dir=$2 fi else $as_nop # 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 echo $i | grep -q /etc if test $? -eq 0 then : iproute_etc_dir=$i fi echo $i | grep -q /usr if test $? -eq 0 then : iproute_usr_dir=$i fi done fi if test .${iproute_etc_dir}${iproute_usr_dir} = . then : iproute_etc_dir=/etc/iproute2 iproute_usr_dir=/usr/share/iproute2 fi fi if test .${iproute_etc_dir} != . then : printf "%s\n" "#define IPROUTE_ETC_DIR \"$iproute_etc_dir\"" >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS IPROUTE_ETC_DIR=$iproute_etc_dir" fi if test .${iproute_usr_dir} != . then : printf "%s\n" "#define IPROUTE_USR_DIR \"$iproute_usr_dir\"" >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS IPROUTE_USR_DIR=$iproute_usr_dir" fi SAV_CFLAGS="$CFLAGS" CFLAGS="-Wformat -Werror=format $SAV_CPPFLAGS $KA_CPPFLAGS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main(int argc, char **argv) { rlim_t val = 23U; printf("%lu %d %p", val, argc, argv); return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define PRI_rlim_t \"lu\"" >>confdefs.h else $as_nop printf "%s\n" "#define PRI_rlim_t \"llu\"" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS="$SAV_CFLAGS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(int argc, char **argv) { struct tcphdr hdr; hdr.th_dport = argc; } _ACEOF if ac_fn_c_try_compile "$LINENO" then : else $as_nop printf "%s\n" "#define NEED_FAVOR_BSD 1 " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext if test -z "$SPHINXBUILD"; then SPHINXBUILDNAME=sphinx-build else SPHINXBUILDNAME=${SPHINXBUILD} fi # Extract the first word of "$SPHINXBUILDNAME", so it can be a program name with args. set dummy $SPHINXBUILDNAME; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_HAVE_SPHINX_BUILD+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$HAVE_SPHINX_BUILD"; then ac_cv_prog_HAVE_SPHINX_BUILD="$HAVE_SPHINX_BUILD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_HAVE_SPHINX_BUILD="Yes" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_HAVE_SPHINX_BUILD" && ac_cv_prog_HAVE_SPHINX_BUILD="No" fi fi HAVE_SPHINX_BUILD=$ac_cv_prog_HAVE_SPHINX_BUILD if test -n "$HAVE_SPHINX_BUILD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $HAVE_SPHINX_BUILD" >&5 printf "%s\n" "$HAVE_SPHINX_BUILD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test $HAVE_SPHINX_BUILD = Yes; then BUILD_DOCS_TRUE= BUILD_DOCS_FALSE='#' else BUILD_DOCS_TRUE='#' BUILD_DOCS_FALSE= fi MEM_CHECK=No MEM_CHECK_LOG=No if test "${enable_mem_check}" = "yes"; then MEM_CHECK=Yes printf "%s\n" "#define _MEM_CHECK_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS MEM_CHECK" if test "${enable_mem_check_log}" = "yes"; then MEM_CHECK_LOG=Yes printf "%s\n" "#define _MEM_CHECK_LOG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS MEM_CHECK_LOG" fi fi MALLOC_CHECK=No if test ."${enable_malloc_check}" = ."yes" then : MALLOC_CHECK=Yes printf "%s\n" "#define _MALLOC_CHECK_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS MALLOC_CHECK" fi OPENSSL_MEM_CHECK=No if test "${enable_openssl_mem_check}" = "yes"; then OPENSSL_MEM_CHECK=Yes printf "%s\n" "#define _OPENSSL_MEM_CHECK_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS OPENSSL_MEM_CHECK" fi TIMER_CHECK=No if test "${enable_timer_check}" = "yes"; then TIMER_CHECK=Yes printf "%s\n" "#define _TIMER_CHECK_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS TIMER_CHECK" fi if test .$enable_vrrp != .no then : FAULT_FLAGS_CHECK=No if test .${enable_fault_flags_check} = .yes then : FAULT_FLAGS_CHECK=Yes printf "%s\n" "#define _FAULT_FLAGS_CHECK_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS FAULT_FLAGS_CHECK" fi fi if test "${enable_one_process_debug}" = yes; then printf "%s\n" "#define _ONE_PROCESS_DEBUG_ 1 " >>confdefs.h ENABLE_ONE_PROCESS_DEBUG=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS ONE_PROCESS_DEBUG" else ENABLE_ONE_PROCESS_DEBUG=No fi if test $ENABLE_ONE_PROCESS_DEBUG = Yes; then ONE_PROCESS_DEBUG_TRUE= ONE_PROCESS_DEBUG_FALSE='#' else ONE_PROCESS_DEBUG_TRUE='#' ONE_PROCESS_DEBUG_FALSE= fi if test "${enable_netlink_timers}" = yes; then printf "%s\n" "#define _NETLINK_TIMERS_ 1 " >>confdefs.h ENABLE_NETLINK_TIMERS=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS NETLINK_TIMERS" else ENABLE_NETLINK_TIMERS=No fi if test "${enable_smtp_alert_debug}" = yes; then printf "%s\n" "#define _SMTP_ALERT_DEBUG_ 1 " >>confdefs.h ENABLE_SMTP_ALERT_DEBUG=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS SMTP_ALERT_DEBUG" ENABLE_LOG_FILE_APPEND=Yes else ENABLE_SMTP_ALERT_DEBUG=No fi if test "${enable_stacktrace}" = yes; then printf "%s\n" "#define _WITH_STACKTRACE_ 1 " >>confdefs.h ENABLE_STACKTRACE=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS STACKTRACE" KA_LDFLAGS="$KA_LDFLAGS -rdynamic" else ENABLE_STACKTRACE=No fi if test "${enable_dump_threads}" = yes; then printf "%s\n" "#define _WITH_DUMP_THREADS_ 1 " >>confdefs.h ENABLE_DUMP_THREADS=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS DUMP_THREADS" else ENABLE_DUMP_THREADS=No fi if test "${enable_epoll_debug}" = yes; then printf "%s\n" "#define _EPOLL_DEBUG_ 1 " >>confdefs.h ENABLE_EPOLL_DEBUG=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS EPOLL_DEBUG" else ENABLE_EPOLL_DEBUG=No fi if test "${enable_epoll_thread_dump}" = yes; then printf "%s\n" "#define _EPOLL_THREAD_DUMP_ 1 " >>confdefs.h ENABLE_EPOLL_THREAD_DUMP=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS EPOLL_THREAD_DUMP" else ENABLE_EPOLL_THREAD_DUMP=No fi if test $ENABLE_EPOLL_THREAD_DUMP = Yes -o $ENABLE_DUMP_THREADS = Yes -o $ENABLE_EPOLL_DEBUG = Yes; then printf "%s\n" "#define THREAD_DUMP 1 " >>confdefs.h fi if test "${enable_tsm_debug}" = yes; then printf "%s\n" "#define _TSM_DEBUG_ 1 " >>confdefs.h ENABLE_TSM_DEBUG=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS TSM_DEBUG" else ENABLE_TSM_DEBUG=No fi if test "${enable_vrrp_fd_debug}" = yes; then printf "%s\n" "#define _VRRP_FD_DEBUG_ 1 " >>confdefs.h ENABLE_VRRP_FD_DEBUG=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS VRRP_FD_DEBUG" else ENABLE_VRRP_FD_DEBUG=No fi if test "${enable_network_timestamp}" = yes; then printf "%s\n" "#define _NETWORK_TIMESTAMP_ 1 " >>confdefs.h ENABLE_NETWORK_TIMESTAMP=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS NETWORK_TIMESTAMP" else ENABLE_NETWORK_TIMESTAMP=No fi if test "${enable_asserts}" = yes; then printf "%s\n" "#define _ENABLE_ASSERT_ 1 " >>confdefs.h ENABLE_ASSERT=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS ASSERT" else ENABLE_ASSERT=No fi if test $ENABLE_ASSERT = Yes; then ASSERTS_TRUE= ASSERTS_FALSE='#' else ASSERTS_TRUE='#' ASSERTS_FALSE= fi if test "${with_fixed_if_type}"; then if test "${with_fixed_if_type}" = yes -o ${with_fixed_if_type} = no; then as_fn_error $? "An interface type must be specified with --with-fixed-if-type" "$LINENO" 5 fi printf "%s\n" "#define _FIXED_IF_TYPE_ \"${with_fixed_if_type}\" " >>confdefs.h FIXED_IF_TYPE=${with_fixed_if_type} CONFIG_OPTIONS="$CONFIG_OPTIONS FIXED_IF_TYPE=${with_fixed_if_type}" else FIXED_IF_TYPE= fi WITH_PROFILING=No if test "${enable_profile}" = yes; then # gprof keepalived/keepalived /gmon.keepalived.PID >/tmp/prof.PID.op to generate profile WITH_PROFILING=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS PROFILING" KA_CFLAGS="$KA_CFLAGS -pg" printf "%s\n" "#define _WITH_PROFILING_ 1 " >>confdefs.h fi if test $WITH_PROFILING = Yes; then PROFILE_TRUE= PROFILE_FALSE='#' else PROFILE_TRUE='#' PROFILE_FALSE= fi if test "${enable_perf}" = yes; then printf "%s\n" "#define _WITH_PERF_ 1 " >>confdefs.h ENABLE_PERF=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS PERF" KA_CFLAGS="$KA_CFLAGS -pg" else ENABLE_PERF=No fi # consider -fsanitize-recover=all or -fsanitize-recover=address -fsanitize=pointer-compare -fsanitize=pointer-subtract (pointer-subtract needs option detect_invalid_pointer_pairs=2) SANITIZERS= if test "${enable_sanitize_address}" = yes then : printf "%s\n" "#define _WITH_SANITIZE_ADDRESS_ 1 " >>confdefs.h ENABLE_SANITIZE_ADDRESS=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS SANITIZE_ADDRESS" SANITIZERS="$SANITIZERS,address" if test "${enable_sanitize_address_options}" != no -a -n "${enable_sanitize_address_options}" then : printf "%s\n" "#define _ASAN_DEFAULT_OPTIONS_ \"${enable_sanitize_address_options}\"" >>confdefs.h fi else $as_nop ENABLE_SANITIZE_ADDRESS=No fi if test "${enable_sanitize_hwaddress}" = yes; then printf "%s\n" "#define _WITH_SANITIZE_HWADDRESS_ 1 " >>confdefs.h ENABLE_SANITIZE_HWADDRESS=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS SANITIZE_HWADDRESS" SANITIZERS="$SANITIZERS,hwaddress" if test "${enable_sanitize_hwaddress_options}" != no -a -n "${enable_sanitize_hwaddress_options}" then : printf "%s\n" "#define _HWASAN_DEFAULT_OPTIONS_ \"${enable_sanitize_hwaddress_options}\"" >>confdefs.h fi else ENABLE_SANITIZE_HWADDRESS=No fi echo enable_Sanitize_undefined = $enable_sanitize_undefined if test "${enable_sanitize_undefined}" = yes; then printf "%s\n" "#define _WITH_SANITIZE_UNDEFINED_ 1 " >>confdefs.h ENABLE_SANITIZE_UNDEFINED=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS SANITIZE_UNDEFINED" KA_CFLAGS="$KA_CFLAGS "--param=max-inline-insns-single=100000"" KA_CFLAGS="$KA_CFLAGS "--param=large-function-growth=500"" SANITIZERS="$SANITIZERS,undefined" if test "${enable_sanitize_undefined_options}" != no -a -n "${enable_sanitize_undefined_options}" then : printf "%s\n" "#define _UBSAN_DEFAULT_OPTIONS_ \"${enable_sanitize_undefined_options}\"" >>confdefs.h fi else ENABLE_SANITIZE_UNDEFINED=No fi if test "${enable_sanitize_memory}" = yes; then printf "%s\n" "#define _WITH_SANITIZE_MEMORY_ 1 " >>confdefs.h ENABLE_SANITIZE_MEMORY=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS SANITIZE_MEMORY" SANITIZERS="$SANITIZERS,memory" if test "${enable_sanitize_memory_options}" != no -a -n "${enable_sanitize_memory_options}" then : printf "%s\n" "#define _MSAN_DEFAULT_OPTIONS_ \"${enable_sanitize_memory_options}\"" >>confdefs.h fi else ENABLE_SANITIZE_MEMORY=No fi if test "${enable_sanitize_leak}" = yes; then printf "%s\n" "#define _WITH_SANITIZE_LEAK_ 1 " >>confdefs.h ENABLE_SANITIZE_LEAK=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS SANITIZE_LEAK" SANITIZERS="$SANITIZERS,leak" if test "${enable_sanitize_leak_options}" != no -a -n "${enable_sanitize_leak_options}" then : printf "%s\n" "#define _LSAN_DEFAULT_OPTIONS_ \"${enable_sanitize_leak_options}\"" >>confdefs.h fi else ENABLE_SANITIZE_LEAK=No fi if test "${enable_sanitize_scudo}" = yes; then printf "%s\n" "#define _WITH_SANITIZE_SCUDO_ 1 " >>confdefs.h ENABLE_SANITIZE_SCUDO=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS SANITIZE_SCUDO" SANITIZERS="$SANITIZERS,scudo" if test "${enable_sanitize_scudo_options}" != no -a -n "${enable_sanitize_scudo_options}" then : printf "%s\n" "#define _SCUDO_DEFAULT_OPTIONS_ \"${enable_sanitize_scudo_options}\"" >>confdefs.h fi else ENABLE_SANITIZE_SCUDO=No fi if test -n "$SANITIZERS" then : KA_CFLAGS="$KA_CFLAGS "-fsanitize=${SANITIZERS:1}"" # Skip leading , KA_CFLAGS="$KA_CFLAGS "-g"" printf "%s\n" "#define _WITH_SANITIZER_ 1 " >>confdefs.h fi if test -n "$SANITIZERS"; then WITH_SANITIZER_TRUE= WITH_SANITIZER_FALSE='#' else WITH_SANITIZER_TRUE='#' WITH_SANITIZER_FALSE= fi if test "${enable_log_file}" = yes; then printf "%s\n" "#define ENABLE_LOG_TO_FILE 1 " >>confdefs.h ENABLE_LOG_FILE_APPEND=Yes CONFIG_OPTIONS="$CONFIG_OPTIONS FILE_LOGGING" fi if test "${ENABLE_LOG_FILE_APPEND}" = Yes; then printf "%s\n" "#define ENABLE_LOG_FILE_APPEND 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS LOG_FILE_APPEND" fi ENABLE_RECVMSG_DEBUG=No if test .$enable_recvmsg_debug = .yes then : printf "%s\n" "#define _RECVMSG_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS RECVMSG_DEBUG" ENABLE_RECVMSG_DEBUG=Yes fi ENABLE_EINTR_DEBUG=No if test .$enable_eintr_debug = .yes then : printf "%s\n" "#define _EINTR_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS EINTR_DEBUG" ENABLE_EINTR_DEBUG=Yes else $as_nop if test .$enable_eintr_debug = .check then : printf "%s\n" "#define CHECK_EINTR 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS EINTR_CHECK" fi fi ENABLE_SCRIPT_DEBUG=No if test .$enable_script_debug = .yes then : printf "%s\n" "#define _SCRIPT_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS SCRIPT_DEBUG" ENABLE_SCRIPT_DEBUG=Yes fi ENABLE_TRACK_PROCESS_DEBUG=No if test .$enable_track_process != .no then : if test .$enable_track_process_debug = .yes then : printf "%s\n" "#define _TRACK_PROCESS_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS TRACK_PROCESS_DEBUG" ENABLE_TRACK_PROCESS_DEBUG=Yes fi fi ENABLE_PARSER_DEBUG=No if test .$enable_parser_debug = .yes then : printf "%s\n" "#define _PARSER_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS PARSER_DEBUG" ENABLE_PARSER_DEBUG=Yes fi ENABLE_CHECKSUM_DEBUG=No if test .$enable_checksum_debug = .yes then : printf "%s\n" "#define _CHECKSUM_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS CHECKSUM_DEBUG" ENABLE_CHECKSUM_DEBUG=Yes fi ENABLE_CHECKER_DEBUG=No if test .$enable_checker_debug = .yes then : printf "%s\n" "#define _CHECKER_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS CHECKER_DEBUG" ENABLE_CHECKER_DEBUG=Yes fi ENABLE_SMTP_CONNECT_DEBUG=No if test .$enable_smtp_connect_debug = .yes then : printf "%s\n" "#define _SMTP_CONNECT_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS SMTP_CONNECT_DEBUG" ENABLE_SMTP_CONNECT_DEBUG=Yes fi ENABLE_MEM_ERR_DEBUG=No if test .$enable_mem_err_debug = .yes then : printf "%s\n" "#define _MEM_ERR_DEBUG_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS MEM_ERR_DEBUG" ENABLE_MEM_ERR_DEBUG=Yes fi if test .$enable_dump_keywords = .yes then : printf "%s\n" "#define _DUMP_KEYWORDS_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS DUMP_KEYWORDS" fi if test "${NEED_LIBDL}" = Yes; then KA_LIBS="$KA_LIBS -ldl" fi echo " $KA_LIBS" | grep -qE -- " -l?pthread " if test $? -eq 0 ;then printf "%s\n" "#define _WITH_PTHREADS_ 1 " >>confdefs.h fi RPM_NO_BIP=1 # Extract the first word of "rpm", so it can be a program name with args. set dummy rpm; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_HAVE_RPM+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$HAVE_RPM"; then ac_cv_prog_HAVE_RPM="$HAVE_RPM" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_HAVE_RPM="Yes" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_HAVE_RPM" && ac_cv_prog_HAVE_RPM="No" fi fi HAVE_RPM=$ac_cv_prog_HAVE_RPM if test -n "$HAVE_RPM"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $HAVE_RPM" >&5 printf "%s\n" "$HAVE_RPM" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi if test $HAVE_RPM = Yes; then # Extract the first word of "rpmbuild", so it can be a program name with args. set dummy rpmbuild; ac_word=$2 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 printf %s "checking for $ac_word... " >&6; } if test ${ac_cv_prog_HAVE_RPMBUILD+y} then : printf %s "(cached) " >&6 else $as_nop if test -n "$HAVE_RPMBUILD"; then ac_cv_prog_HAVE_RPMBUILD="$HAVE_RPMBUILD" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then ac_cv_prog_HAVE_RPMBUILD="Yes" printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_prog_HAVE_RPMBUILD" && ac_cv_prog_HAVE_RPMBUILD="No" fi fi HAVE_RPMBUILD=$ac_cv_prog_HAVE_RPMBUILD if test -n "$HAVE_RPMBUILD"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $HAVE_RPMBUILD" >&5 printf "%s\n" "$HAVE_RPMBUILD" >&6; } else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } fi RPM_SRC_DIR=`rpm --eval "%{_sourcedir}"` if ! test -d $RPM_SRC_DIR; then HAVE_RPMBUILD=No fi if test $HAVE_RPMBUILD = Yes; then rpmbuild --help | grep -q -- --build-in-place RPM_NO_BIP=$? fi fi if test $HAVE_RPM = Yes; then RPM_TRUE= RPM_FALSE='#' else RPM_TRUE='#' RPM_FALSE= fi if test $RPM_NO_BIP -eq 0; then RPM_BIP_TRUE= RPM_BIP_FALSE='#' else RPM_BIP_TRUE='#' RPM_BIP_FALSE= fi INIT_TYPE= if test -n "$init_type" then : INIT_TYPE=$init_type elif test -n "$with_systemdsystemunitdir" then : INIT_TYPE=systemd else $as_nop /sbin/init --version 2>/dev/null | grep -q upstart if test $? -eq 0 then : INIT_TYPE=upstart else $as_nop init_path=`which systemctl 2>/dev/null` if test \( $? -eq 0 -a -x "$init_path" \) then : systemctl | grep -q -- "-\.mount" if test $? -eq 0 then : INIT_TYPE=systemd fi fi if test \( -z "$INIT_TYPE" -a -f /etc/init.d/networking \) then : init_path=`which openrc-run 2>/dev/null` if test \( $? -eq 0 -a -x "$init_path" \) then : head -1 /etc/init.d/networking | grep -q "^#! */.*/openrc-run$" if test $? -eq 0 then : INIT_TYPE=openrc fi fi fi if test -z "$INIT_TYPE" then : for f in /etc/init.d/cron* /etc/init.d/*syslog*; do INIT_TYPE=SYSV break done fi fi fi if test .$INIT_TYPE != . then : CONFIG_OPTIONS="$CONFIG_OPTIONS INIT=$INIT_TYPE" fi if test .$INIT_TYPE = .systemd then : if test -z "$with_systemdsystemunitdir" -o \ .$with_systemdsystemunitdir = .yes -o \ .$with_systemdsystemunitdir = .auto then : def_systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd` if test -z "$def_systemdsystemunitdir" then : if test .$with_systemdsystemunitdir = .yes then : as_fn_error $? "systemd support requested but pkg-config unable to query systemd package" "$LINENO" 5 fi with_systemdsystemunitdir=no else $as_nop with_systemdsystemunitdir="$def_systemdsystemunitdir" fi fi if test .$with_systemdsystemunitdir != .no then : systemdsystemunitdir=$with_systemdsystemunitdir fi fi USE_SYSTEMD_NOTIFY=No SYSTEMD_SERVICE_TYPE=forking SYSTEMD_EXEC_START_OPTIONS= if test .$INIT_TYPE = .systemd then : if test .${enable_systemd} != .no then : $PKG_CONFIG --exists libsystemd if test $? -eq 0 then : USE_SYSTEMD_NOTIFY=Yes if test -n ""; then KA_PKG_PFX= else KA_PKG_PFX=KA fi ADD_NEW= eval var=\$${KA_PKG_PFX}_CPPFLAGS for item in `$PKG_CONFIG --cflags-only-I libsystemd`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CPPFLAGS=\"\$${KA_PKG_PFX}_CPPFLAGS $ADD_NEW\" ADD_NEW= eval var=\$${KA_PKG_PFX}_CFLAGS for item in `$PKG_CONFIG --cflags-only-other libsystemd`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_CFLAGS=\"\$${KA_PKG_PFX}_CFLAGS $ADD_NEW\" if test . = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires libsystemd` var=`$PKG_CONFIG --libs-only-l libsystemd` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else ADD_NEW= eval var=\$${KA_PKG_PFX}_LIBS for item in `$PKG_CONFIG --libs libsystemd`; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then ADD_NEW="$ADD_NEW $item" fi done eval ${KA_PKG_PFX}_LIBS=\"\$${KA_PKG_PFX}_LIBS $ADD_NEW\" fi printf "%s\n" "#define _USE_SYSTEMD_NOTIFY_ 1 " >>confdefs.h CONFIG_OPTIONS="$CONFIG_OPTIONS SYSTEMD_NOTIFY" SYSTEMD_SERVICE_TYPE="notify" SYSTEMD_EXEC_START_OPTIONS+="--dont-fork" SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -Werror" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main(void) { sd_pid_notify(0, 1, ""); } _ACEOF if ac_fn_c_try_compile "$LINENO" then : printf "%s\n" "#define HAVE_SD_PID_NOTIFY 1 " >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext CFLAGS=$SAV_CFLAGS else $as_nop if test .${enable_systemd} = .yes then : { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: libsystemd missing" >&5 printf "%s\n" "$as_me: WARNING: libsystemd missing" >&2;} fi fi fi fi if test $USE_SYSTEMD_NOTIFY = Yes; then WITH_SYSTEMD_NOTIFY_TRUE= WITH_SYSTEMD_NOTIFY_FALSE='#' else WITH_SYSTEMD_NOTIFY_TRUE='#' WITH_SYSTEMD_NOTIFY_FALSE= fi KEEPALIVED_RUNTIME_OPTIONS="$default_runtime_options" echo $default_config_file | grep -q '${' if test $? -eq 0 then : PARAM=`echo $default_config_file | sed -e 's:.*\(${.*}\).*:\1:'` PARAM_BRACKETS=`echo $PARAM | sed -e "s:{:(:" -e "s:}:):"` { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: --with-default-config-file specified with $PARAM - please use $PARAM_BRACKETS" >&5 printf "%s\n" "$as_me: WARNING: --with-default-config-file specified with $PARAM - please use $PARAM_BRACKETS" >&2;} default_config_file=`echo $default_config_file | sed -e "s:{:(:" -e "s:}:):"` fi WANT_PREFIX=0 FOUND_PREFIX=0 # $prefix defaults to NONE, which we need to get rid of sysconfdir_real=${sysconfdir} # while [[[ $sysconfdir_real =~ '\${' ]]]; do # eval sysconfdir_real=$sysconfdir_real # done sysconfdir_sav=${sysconfdir_real} eval sysconfdir_real=$sysconfdir_real while [ $sysconfdir_real != $sysconfdir_sav ]; do sysconfdir_sav=${sysconfdir_real} eval sysconfdir_real=$sysconfdir_real done if test ${sysconfdir_real:0:5} = "NONE/" then : sysconfdir_real=${sysconfdir_real:4} fi if test -n "$default_config_file" then : if test $default_config_file = yes -o $default_config_file = no then : as_fn_error $? "A filename must be specified for default-config-file" "$LINENO" 5 fi FIRST_CHAR=`echo $default_config_file | $SED -e "s/\(.\).*/\1/"` if test $FIRST_CHAR = / -o $FIRST_CHAR = '$' then : CONFIG_FILE="$default_config_file" WANT_PREFIX=1 else $as_nop CONFIG_FILE=${sysconfdir_real}/$PACKAGE/$default_config_file fi CONFIG_OPTIONS="$CONFIG_OPTIONS DEFAULT_CONFIG_FILE=$CONFIG_FILE" default_config_file=$CONFIG_FILE else $as_nop default_config_file=${sysconfdir_real}/$PACKAGE/$PACKAGE.conf fi dir=`echo $default_config_file | $SED -e "s|/[^/]*$||"` name=`echo $default_config_file | $SED -e "s|.*/||"` DEFAULT_CONFIG_FILE=$default_config_file DEFAULT_CONFIG_DIR=${dir} DEFAULT_CONFIG_FILENAME=${name} # Change any make $(...) variable to the matching shell variable and substitute default_config_file=`echo $default_config_file | $SED -e "s|\\\$(\([^)]*\))|\\\${\1}|g"` dcf=$default_config_file dcf_prev=x$dcf # to force at least 1 iteration while [ $dcf != $dcf_prev ]; do echo $dcf | grep -q "^\\\${prefix}" if test $? -eq 0 then : FOUND_PREFIX=1 elif test -n "${prefix}" then : echo $dcf | grep -q "^${prefix}/" if test $? -eq 0 then : FOUND_PREFIX=1 fi fi dcf_prev=$dcf dcf=`eval echo $dcf` done if test $WANT_PREFIX -eq 1 -a $FOUND_PREFIX -eq 0 then : dcf=${prefix}$dcf fi dcf=`echo $dcf | $SED -e "s://*:/:g"` printf "%s\n" "#define DEFAULT_CONFIG_FILE \"$dcf\"" >>confdefs.h # Remove a leading ${prefix} since that is not part of the old file name eval dcf_old=`echo $default_config_file` dcf_old=`echo $dcf_old | $SED -e "s|^${prefix}||"` if test .$dcf != .$dcf_old then : printf "%s\n" "#define OLD_DEFAULT_CONFIG_FILE \"$dcf_old\"" >>confdefs.h OLD_DEFAULT_CONFIG_FILE=$dcf_old fi if test .$enable_reproducible_build = .yes then : printf "%s\n" "#define _REPRODUCIBLE_BUILD_ 1 " >>confdefs.h KEEPALIVED_CONFIG_OPTIONS="$args" primary_config_opts=${sysconfdir_real}/$PACKAGE/$PACKAGE.config-opts printf "%s\n" "#define CONFIG_OPTS_FILE_PRIMARY \"$primary_config_opts\"" >>confdefs.h fi if test .$enable_reproducible_build = .yes; then REPRODUCIBLE_BUILD_TRUE= REPRODUCIBLE_BUILD_FALSE='#' else REPRODUCIBLE_BUILD_TRUE='#' REPRODUCIBLE_BUILD_FALSE= fi if test -z "$INIT_TYPE"; then INIT_TYPE=undetected elif test $INIT_TYPE = systemd; then systemdsystemunitdir=$with_systemdsystemunitdir fi if test $INIT_TYPE = upstart; then INIT_UPSTART_TRUE= INIT_UPSTART_FALSE='#' else INIT_UPSTART_TRUE='#' INIT_UPSTART_FALSE= fi if test $INIT_TYPE = systemd; then INIT_SYSTEMD_TRUE= INIT_SYSTEMD_FALSE='#' else INIT_SYSTEMD_TRUE='#' INIT_SYSTEMD_FALSE= fi if test $INIT_TYPE = SYSV; then INIT_SYSV_TRUE= INIT_SYSV_FALSE='#' else INIT_SYSV_TRUE='#' INIT_SYSV_FALSE= fi if test $INIT_TYPE = openrc; then INIT_OPENRC_TRUE= INIT_OPENRC_FALSE='#' else INIT_OPENRC_TRUE='#' INIT_OPENRC_FALSE= fi if test $INIT_TYPE = SUSE; then INIT_SUSE_TRUE= INIT_SUSE_FALSE='#' else INIT_SUSE_TRUE='#' INIT_SUSE_FALSE= fi printf "%s\n" "#define CONFIGURATION_OPTIONS \"$CONFIG_OPTIONS\"" >>confdefs.h printf "%s\n" "#define SYSTEM_OPTIONS \"$SYSTEM_OPTIONS\"" >>confdefs.h if test $NETLINK_VER -eq 0; then NETLINK_VER=None fi echo # Tidy up some strings KA_CPPFLAGS=`echo $KA_CPPFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_CFLAGS=`echo $KA_CFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_LDFLAGS=`echo $KA_LDFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_LIBS=`echo $KA_LIBS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` # Tidy up some strings KA_CPPFLAGS=`echo $KA_CPPFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_CFLAGS=`echo $KA_CFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_LDFLAGS=`echo $KA_LDFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_LIBS=`echo $KA_LIBS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 printf "%s\n" "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 printf %s "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: done" >&5 printf "%s\n" "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${DEBUG_TRUE}" && test -z "${DEBUG_FALSE}"; then as_fn_error $? "conditional \"DEBUG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_STRICT_CONFIG_CHECKS_TRUE}" && test -z "${WITH_STRICT_CONFIG_CHECKS_FALSE}"; then as_fn_error $? "conditional \"WITH_STRICT_CONFIG_CHECKS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LIBNL1_TRUE}" && test -z "${LIBNL1_FALSE}"; then as_fn_error $? "conditional \"LIBNL1\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LIBNL3_TRUE}" && test -z "${LIBNL3_FALSE}"; then as_fn_error $? "conditional \"LIBNL3\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LIBNL_DYNAMIC_TRUE}" && test -z "${LIBNL_DYNAMIC_FALSE}"; then as_fn_error $? "conditional \"LIBNL_DYNAMIC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${MAGIC_TRUE}" && test -z "${MAGIC_FALSE}"; then as_fn_error $? "conditional \"MAGIC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LIBIPSET_TRUE}" && test -z "${LIBIPSET_FALSE}"; then as_fn_error $? "conditional \"LIBIPSET\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${IPTABLES_TRUE}" && test -z "${IPTABLES_FALSE}"; then as_fn_error $? "conditional \"IPTABLES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LIBIPTC_DYNAMIC_TRUE}" && test -z "${LIBIPTC_DYNAMIC_FALSE}"; then as_fn_error $? "conditional \"LIBIPTC_DYNAMIC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${LIBIPSET_DYNAMIC_TRUE}" && test -z "${LIBIPSET_DYNAMIC_FALSE}"; then as_fn_error $? "conditional \"LIBIPSET_DYNAMIC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NFTABLES_TRUE}" && test -z "${NFTABLES_FALSE}"; then as_fn_error $? "conditional \"NFTABLES\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${FIREWALL_TRUE}" && test -z "${FIREWALL_FALSE}"; then as_fn_error $? "conditional \"FIREWALL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_IPVS_TRUE}" && test -z "${WITH_IPVS_FALSE}"; then as_fn_error $? "conditional \"WITH_IPVS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_REGEX_TRUE}" && test -z "${WITH_REGEX_FALSE}"; then as_fn_error $? "conditional \"WITH_REGEX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_VRRP_TRUE}" && test -z "${WITH_VRRP_FALSE}"; then as_fn_error $? "conditional \"WITH_VRRP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${VRRP_AUTH_TRUE}" && test -z "${VRRP_AUTH_FALSE}"; then as_fn_error $? "conditional \"VRRP_AUTH\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${VMAC_TRUE}" && test -z "${VMAC_FALSE}"; then as_fn_error $? "conditional \"VMAC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_JSON_TRUE}" && test -z "${WITH_JSON_FALSE}"; then as_fn_error $? "conditional \"WITH_JSON\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_BFD_TRUE}" && test -z "${WITH_BFD_FALSE}"; then as_fn_error $? "conditional \"WITH_BFD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${TRACK_PROCESS_TRUE}" && test -z "${TRACK_PROCESS_FALSE}"; then as_fn_error $? "conditional \"TRACK_PROCESS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${NETWORK_MANAGER_TRUE}" && test -z "${NETWORK_MANAGER_FALSE}"; then as_fn_error $? "conditional \"NETWORK_MANAGER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SNMP_TRUE}" && test -z "${SNMP_FALSE}"; then as_fn_error $? "conditional \"SNMP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SNMP_KEEPALIVED_TRUE}" && test -z "${SNMP_KEEPALIVED_FALSE}"; then as_fn_error $? "conditional \"SNMP_KEEPALIVED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SNMP_VRRP_TRUE}" && test -z "${SNMP_VRRP_FALSE}"; then as_fn_error $? "conditional \"SNMP_VRRP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SNMP_CHECKER_TRUE}" && test -z "${SNMP_CHECKER_FALSE}"; then as_fn_error $? "conditional \"SNMP_CHECKER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SNMP_RFC_TRUE}" && test -z "${SNMP_RFC_FALSE}"; then as_fn_error $? "conditional \"SNMP_RFC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SNMP_RFCV2_TRUE}" && test -z "${SNMP_RFCV2_FALSE}"; then as_fn_error $? "conditional \"SNMP_RFCV2\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SNMP_RFCV3_TRUE}" && test -z "${SNMP_RFCV3_FALSE}"; then as_fn_error $? "conditional \"SNMP_RFCV3\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${SNMP_REPLY_V3_FOR_V2_TRUE}" && test -z "${SNMP_REPLY_V3_FOR_V2_FALSE}"; then as_fn_error $? "conditional \"SNMP_REPLY_V3_FOR_V2\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_DBUS_TRUE}" && test -z "${WITH_DBUS_FALSE}"; then as_fn_error $? "conditional \"WITH_DBUS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${DBUS_CREATE_INSTANCE_TRUE}" && test -z "${DBUS_CREATE_INSTANCE_FALSE}"; then as_fn_error $? "conditional \"DBUS_CREATE_INSTANCE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${BUILD_DOCS_TRUE}" && test -z "${BUILD_DOCS_FALSE}"; then as_fn_error $? "conditional \"BUILD_DOCS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ONE_PROCESS_DEBUG_TRUE}" && test -z "${ONE_PROCESS_DEBUG_FALSE}"; then as_fn_error $? "conditional \"ONE_PROCESS_DEBUG\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${ASSERTS_TRUE}" && test -z "${ASSERTS_FALSE}"; then as_fn_error $? "conditional \"ASSERTS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${PROFILE_TRUE}" && test -z "${PROFILE_FALSE}"; then as_fn_error $? "conditional \"PROFILE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_SANITIZER_TRUE}" && test -z "${WITH_SANITIZER_FALSE}"; then as_fn_error $? "conditional \"WITH_SANITIZER\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${RPM_TRUE}" && test -z "${RPM_FALSE}"; then as_fn_error $? "conditional \"RPM\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${RPM_BIP_TRUE}" && test -z "${RPM_BIP_FALSE}"; then as_fn_error $? "conditional \"RPM_BIP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${WITH_SYSTEMD_NOTIFY_TRUE}" && test -z "${WITH_SYSTEMD_NOTIFY_FALSE}"; then as_fn_error $? "conditional \"WITH_SYSTEMD_NOTIFY\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${REPRODUCIBLE_BUILD_TRUE}" && test -z "${REPRODUCIBLE_BUILD_FALSE}"; then as_fn_error $? "conditional \"REPRODUCIBLE_BUILD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${INIT_UPSTART_TRUE}" && test -z "${INIT_UPSTART_FALSE}"; then as_fn_error $? "conditional \"INIT_UPSTART\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${INIT_SYSTEMD_TRUE}" && test -z "${INIT_SYSTEMD_FALSE}"; then as_fn_error $? "conditional \"INIT_SYSTEMD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${INIT_SYSV_TRUE}" && test -z "${INIT_SYSV_FALSE}"; then as_fn_error $? "conditional \"INIT_SYSV\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${INIT_OPENRC_TRUE}" && test -z "${INIT_OPENRC_FALSE}"; then as_fn_error $? "conditional \"INIT_OPENRC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${INIT_SUSE_TRUE}" && test -z "${INIT_SUSE_FALSE}"; then as_fn_error $? "conditional \"INIT_SUSE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh as_nop=: if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else $as_nop case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi # Reset variables that may have inherited troublesome values from # the environment. # IFS needs to be set, to space, tab, and newline, in precisely that order. # (If _AS_PATH_WALK were called with IFS unset, it would have the # side effect of setting IFS to empty, thus disabling word splitting.) # Quoting is to prevent editors from complaining about space-tab. as_nl=' ' export as_nl IFS=" "" $as_nl" PS1='$ ' PS2='> ' PS4='+ ' # Ensure predictable behavior from utilities with locale-dependent output. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # We cannot yet rely on "unset" to work, but we need these variables # to be unset--not just set to an empty or harmless value--now, to # avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct # also avoids known problems related to "unset" and subshell syntax # in other old shells (e.g. bash 2.01 and pdksh 5.2.14). for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH do eval test \${$as_var+y} \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done # Ensure that fds 0, 1, and 2 are open. if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi if (exec 3>&2) ; then :; else exec 2>/dev/null; fi # The user is always right. if ${PATH_SEPARATOR+false} :; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS case $as_dir in #((( '') as_dir=./ ;; */) ;; *) as_dir=$as_dir/ ;; esac test -r "$as_dir$0" && as_myself=$as_dir$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi printf "%s\n" "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null then : eval 'as_fn_append () { eval $1+=\$2 }' else $as_nop as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null then : eval 'as_fn_arith () { as_val=$(( $* )) }' else $as_nop as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits # Determine whether it's possible to make 'echo' print without a newline. # These variables are no longer used directly by Autoconf, but are AC_SUBSTed # for compatibility with existing Makefiles. ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac # For backward compatibility with old third-party macros, we provide # the shell variables $as_echo and $as_echo_n. New code should use # AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. as_echo='printf %s\n' as_echo_n='printf %s' rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by Keepalived $as_me 2.3.3, which was generated by GNU Autoconf 2.71. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to . Keepalived home page: ." _ACEOF ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ Keepalived config.status 2.3.3 configured by $0, generated by GNU Autoconf 2.71, with options \\"\$ac_cs_config\\" Copyright (C) 2021 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) printf "%s\n" "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) printf "%s\n" "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) printf "%s\n" "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX printf "%s\n" "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "lib/config.h") CONFIG_HEADERS="$CONFIG_HEADERS lib/config.h" ;; "lib/config_warnings.h") CONFIG_HEADERS="$CONFIG_HEADERS lib/config_warnings.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "keepalived/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/Makefile" ;; "lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;; "keepalived/core/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/core/Makefile" ;; "keepalived.spec") CONFIG_FILES="$CONFIG_FILES keepalived.spec" ;; "Dockerfile") CONFIG_FILES="$CONFIG_FILES Dockerfile" ;; "keepalived/check/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/check/Makefile" ;; "keepalived/vrrp/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/vrrp/Makefile" ;; "keepalived/bfd/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/bfd/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "bin_install/Makefile") CONFIG_FILES="$CONFIG_FILES bin_install/Makefile" ;; "keepalived/dbus/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/dbus/Makefile" ;; "keepalived/etc/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/etc/Makefile" ;; "keepalived/etc/init/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/etc/init/Makefile" ;; "keepalived/etc/init.d/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/etc/init.d/Makefile" ;; "keepalived/etc/sysconfig/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/etc/sysconfig/Makefile" ;; "keepalived/etc/keepalived/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/etc/keepalived/Makefile" ;; "keepalived/trackers/Makefile") CONFIG_FILES="$CONFIG_FILES keepalived/trackers/Makefile" ;; "doc/man/man8/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/man8/Makefile" ;; "doc/man/man5/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/man5/Makefile" ;; "doc/man/man1/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/man1/Makefile" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 printf "%s\n" "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`printf "%s\n" "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else printf "%s\n" "/* $configure_input */" >&1 \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 printf "%s\n" "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. case $CONFIG_FILES in #( *\'*) : eval set x "$CONFIG_FILES" ;; #( *) : set x $CONFIG_FILES ;; #( *) : ;; esac shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`printf "%s\n" "$am_mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`$as_dirname -- "$am_mf" || $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$am_mf" : 'X\(//\)[^/]' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` am_filepart=`$as_basename -- "$am_mf" || $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || printf "%s\n" X/"$am_mf" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` { echo "$as_me:$LINENO: cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles" >&5 (cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } || am_rc=$? done if test $am_rc -ne 0; then { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE=\"gmake\" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } fi { am_dirpart=; unset am_dirpart;} { am_filepart=; unset am_filepart;} { am_mf=; unset am_mf;} { am_rc=; unset am_rc;} rm -f conftest-deps.mk } ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi cat <, grant the following special exception: I, Alexandre Cassen, , explicitly allow the compilation and distribution of the Keepalived software with the OpenSSL Toolkit. keepalived-2.3.3/m4/0000775000175000017500000000000014772274311007723 5keepalived-2.3.3/m4/pkg.m40000644000175000017500000002400714435621316010664 # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR keepalived-2.3.3/m4/as-ac-expand.m40000664000175000017500000000255314122410572012341 dnl as-ac-expand.m4 0.2.0 -*- autoconf -*- dnl autostars m4 macro for expanding directories using configure's prefix dnl (C) 2003, 2004, 2005 Thomas Vander Stichele dnl Copying and distribution of this file, with or without modification, dnl are permitted in any medium without royalty provided the copyright dnl notice and this notice are preserved. dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR) dnl example: dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir) dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local AC_DEFUN([AS_AC_EXPAND], [ EXP_VAR=[$1] FROM_VAR=[$2] dnl first expand prefix and exec_prefix if necessary prefix_save=$prefix exec_prefix_save=$exec_prefix dnl if no prefix given, then use /usr/local, the default prefix if test "x$prefix" = "xNONE"; then prefix="$ac_default_prefix" fi dnl if no exec_prefix given, then use prefix if test "x$exec_prefix" = "xNONE"; then exec_prefix=$prefix fi full_var="$FROM_VAR" dnl loop until it doesn't change anymore while true; do new_full_var="`eval echo $full_var`" if test "x$new_full_var" = "x$full_var"; then break; fi full_var=$new_full_var done dnl clean up full_var=$new_full_var AC_SUBST([$1], "$full_var") dnl restore prefix and exec_prefix prefix=$prefix_save exec_prefix=$exec_prefix_save ]) keepalived-2.3.3/build-aux/0000775000175000017500000000000014772274311011275 5keepalived-2.3.3/build-aux/depcomp0000755000175000017500000005602014556763366012610 #! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2021 Free Software Foundation, Inc. # 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, or (at your option) # any later version. # 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. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: keepalived-2.3.3/build-aux/missing0000755000175000017500000001533614556763366012637 #! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2021 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # 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, or (at your option) # any later version. # 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. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: keepalived-2.3.3/build-aux/install-sh0000755000175000017500000003577614556763366013256 #!/bin/sh # install - install a program, script, or datafile scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 # Create dirs (including intermediate dirs) using mode 755. # This is like GNU 'install' as of coreutils 8.32 (2020). mkdir_umask=22 backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -p pass -p to $cpprog. -s $stripprog installed files. -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG By default, rm is invoked with -f; when overridden with RMPROG, it's up to you to specify -f if you want it. If -S is not specified, no backups are attempted. Email bug reports to bug-automake@gnu.org. Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; -S) backupsuffix="$2" shift;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? # Don't chown directories that already exist. if test $dstdir_status = 0; then chowncmd="" fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false # The $RANDOM variable is not portable (e.g., dash). Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap ' ret=$? rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null exit $ret ' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p'. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && { test -z "$stripcmd" || { # Create $dsttmp read-write so that cp doesn't create it read-only, # which would cause strip to fail. if test -z "$doit"; then : >"$dsttmp" # No need to fork-exec 'touch'. else $doit touch "$dsttmp" fi } } && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # If $backupsuffix is set, and the file being installed # already exists, attempt a backup. Don't worry if it fails, # e.g., if mv doesn't support -f. if test -n "$backupsuffix" && test -f "$dst"; then $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null fi # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: keepalived-2.3.3/build-aux/ar-lib0000755000175000017500000001336314556763366012332 #! /bin/sh # Wrapper for Microsoft lib.exe me=ar-lib scriptversion=2019-07-04.01; # UTC # Copyright (C) 2010-2021 Free Software Foundation, Inc. # Written by Peter Rosin . # # 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, or (at your option) # any later version. # # 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. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # func_error message func_error () { echo "$me: $1" 1>&2 exit 1 } file_conv= # func_file_conv build_file # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN* | MSYS*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv in mingw) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin | msys) file=`cygpath -m "$file" || echo "$file"` ;; wine) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_at_file at_file operation archive # Iterate over all members in AT_FILE performing OPERATION on ARCHIVE # for each of them. # When interpreting the content of the @FILE, do NOT use func_file_conv, # since the user would need to supply preconverted file names to # binutils ar, at least for MinGW. func_at_file () { operation=$2 archive=$3 at_file_contents=`cat "$1"` eval set x "$at_file_contents" shift for member do $AR -NOLOGO $operation:"$member" "$archive" || exit $? done } case $1 in '') func_error "no command. Try '$0 --help' for more information." ;; -h | --h*) cat <. # # 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, or (at your option) # any later version. # # 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. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN* | MSYS*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/* | msys/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: keepalived-2.3.3/Dockerfile.in0000664000175000017500000000526114713273033011721 FROM alpine:latest ARG GIT_VER= ENV VER=@VERSION@ LABEL version=@VERSION@${GIT_VER} LABEL author="Alexandre Cassen " LABEL project="https://github.com/acassen/keepalived" LABEL homepage="https://www.keepalived.org" # add keepalived sources to /tmp/keepalived-@VERSION@ ADD keepalived-@VERSION@.tar.gz /tmp # Add keepalived default script user to make sure their IDs get assigned consistently, # regardless of whatever dependencies get added RUN addgroup -S keepalived_script && adduser -D -S -G keepalived_script keepalived_script # 1. install required libraries and tools # 2. compile and install keepalived # 3. remove keepalived sources and unnecessary libraries and tools RUN apk --no-cache add \ binutils \ @MAGIC_TRUE@ file \ @MAGIC_TRUE@ file-dev \ gcc \ @WITH_DBUS_TRUE@ glib \ @WITH_DBUS_TRUE@ glib-dev \ @LIBIPSET_TRUE@ ipset \ @LIBIPSET_TRUE@ ipset-dev \ @IPTABLES_TRUE@ iptables \ @IPTABLES_TRUE@ iptables-dev \ @NFTABLES_TRUE@ libmnl-dev \ @NFTABLES_TRUE@ libnftnl-dev \ libnl3 \ libnl3-dev \ linux-headers \ make \ musl-dev \ @SNMP_TRUE@ net-snmp-dev \ openssl \ openssl-dev \ @WITH_REGEX_TRUE@ pcre2 \ @WITH_REGEX_TRUE@ pcre2-dev \ autoconf \ automake \ && cd /tmp/keepalived-@VERSION@/ \ && ./autogen.sh \ && ./configure \ --disable-dynamic-linking \ --prefix=/usr \ --exec-prefix=/usr \ --bindir=/usr/bin \ --sbindir=/usr/sbin \ --sysconfdir=/etc \ --datadir=/usr/share \ --localstatedir=/var \ --mandir=/usr/share/man \ @WITH_DBUS_TRUE@ --with-dbus-data-dir=/usr/share \ --enable-bfd \ @WITH_DBUS_TRUE@ --enable-dbus \ @WITH_REGEX_TRUE@ --enable-regex \ @SNMP_TRUE@ --enable-snmp \ @SNMP_TRUE@ --enable-snmp-rfc \ @NFTABLES_TRUE@ --enable-nftables \ @IPTABLES_FALSE@ --disable-iptables \ @IPTABLES_TRUE@@LIBIPSET_FALSE@ --disable-libipset \ @WITH_JSON_TRUE@ --enable-json \ && make && make install \ && strip /usr/sbin/keepalived \ && cd - \ && rm -rf /tmp/keepalived-@VERSION@ \ && apk --no-cache del \ binutils \ @MAGIC_TRUE@ file-dev \ gcc \ @WITH_DBUS_TRUE@ glib-dev \ @LIBIPSET_TRUE@ ipset-dev \ @IPTABLES_TRUE@ iptables-dev \ @NFTABLES_TRUE@ libmnl-dev \ libnl3-dev \ @NFTABLES_TRUE@ libnftnl-dev \ make \ musl-dev \ openssl-dev \ @WITH_REGEX_TRUE@ pcre2-dev \ autoconf \ automake ADD docker/keepalived.conf /etc/keepalived/keepalived.conf # set keepalived as image entrypoint with --dont-fork and --log-console (to make it docker friendly) # define /etc/keepalived/keepalived.conf as the configuration file to use ENTRYPOINT ["/usr/sbin/keepalived","--dont-fork","--log-console", "-f","/etc/keepalived/keepalived.conf"] # example command to customise keepalived daemon: # CMD ["--log-detail","--dump-conf"] keepalived-2.3.3/keepalived/0000775000175000017500000000000014772274312011515 5keepalived-2.3.3/keepalived/vrrp/0000775000175000017500000000000014772274312012506 5keepalived-2.3.3/keepalived/vrrp/vrrp_sync.c0000664000175000017500000001640314105530426014612 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: VRRP synchronization framework. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include "vrrp_sync.h" #include "vrrp_track.h" #include "vrrp_notify.h" #include "vrrp_data.h" #include "logger.h" #include "vrrp_scheduler.h" #include "parser.h" /* Instance name lookup */ vrrp_t * __attribute__ ((pure)) vrrp_get_instance(char *iname) { vrrp_t *vrrp; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (strcmp(vrrp->iname, iname) == 0) return vrrp; } return NULL; } /* Set instances group pointer */ bool vrrp_sync_set_group(vrrp_sgroup_t *sgroup) { vrrp_t *vrrp; char *str; unsigned int i; bool group_member_down = false; /* Can't handle no members of the group */ if (!sgroup->iname) return false; for (i = 0; i < vector_size(sgroup->iname); i++) { str = vector_slot(sgroup->iname, i); vrrp = vrrp_get_instance(str); if (!vrrp) { report_config_error(CONFIG_GENERAL_ERROR, "Virtual router %s specified in sync group %s doesn't exist - ignoring", str, sgroup->gname); continue; } if (vrrp->sync) { report_config_error(CONFIG_GENERAL_ERROR, "Virtual router %s cannot exist in more than one sync group; ignoring %s", str, sgroup->gname); continue; } list_add_tail(&vrrp->s_list, &sgroup->vrrp_instances); vrrp->sync = sgroup; /* set eventual sync group state. Unless all members are master and address owner, * then we must be backup */ if (sgroup->state == VRRP_STATE_MAST && vrrp->wantstate == VRRP_STATE_BACK) report_config_error(CONFIG_GENERAL_ERROR, "Sync group %s has some member(s) as address owner and some not as address owner. This won't work.", sgroup->gname); if (sgroup->state != VRRP_STATE_BACK) sgroup->state = (vrrp->wantstate == VRRP_STATE_MAST && vrrp->base_priority == VRRP_PRIO_OWNER) ? VRRP_STATE_MAST : VRRP_STATE_BACK; // TODO - what about track scripts down? if (vrrp->state == VRRP_STATE_FAULT) group_member_down = true; } /* The iname vector is only used for us to set up the sync groups, so delete it */ free_strvec(sgroup->iname); sgroup->iname = NULL; if (group_member_down) sgroup->state = VRRP_STATE_FAULT; /* The sync group will be removed by the calling function if it has no members */ if (list_empty(&sgroup->vrrp_instances)) { report_config_error(CONFIG_GENERAL_ERROR, "Sync group %s, no matching virtual router found" " in group declaration - removing" , sgroup->gname); return false; } /* For most users a sync group with only one member is a configuration error */ if (sgroup->vrrp_instances.prev == sgroup->vrrp_instances.next) report_config_error(CONFIG_GENERAL_ERROR, "Sync group %s has only 1 virtual router(s) -" " this probably isn't what you want" , sgroup->gname); return true; } /* Check transition to master state */ bool vrrp_sync_can_goto_master(vrrp_t *vrrp) { vrrp_sgroup_t *sgroup = vrrp->sync; vrrp_t *isync; if (GROUP_STATE(sgroup) == VRRP_STATE_MAST) return true; /* Only sync to master if everyone wants to * i.e. prefer backup state to avoid thrashing */ list_for_each_entry(isync, &sgroup->vrrp_instances, s_list) { if (isync != vrrp && isync->wantstate != VRRP_STATE_MAST) { /* Make sure we give time for other instances to be * ready to become master. The timer here doesn't * really matter, since we are waiting for other * instances to be ready. */ vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); vrrp_init_instance_sands(vrrp); return false; } } return true; } void vrrp_sync_backup(vrrp_t *vrrp) { vrrp_sgroup_t *sgroup = vrrp->sync; vrrp_t *isync; if (GROUP_STATE(sgroup) == VRRP_STATE_BACK) return; log_message(LOG_INFO, "VRRP_Group(%s) Syncing instances to BACKUP state" , GROUP_NAME(sgroup)); /* Perform sync index */ list_for_each_entry(isync, &sgroup->vrrp_instances, s_list) { if (isync == vrrp || isync->state == VRRP_STATE_BACK) continue; isync->wantstate = VRRP_STATE_BACK; // TODO - we may be leaving FAULT, so calling leave_master isn't right. I have // had to add vrrp_state_leave_fault() for this if (isync->state == VRRP_STATE_FAULT || isync->state == VRRP_STATE_INIT) { vrrp_state_leave_fault(isync); } else vrrp_state_leave_master(isync, false); vrrp_thread_requeue_read(isync); } sgroup->state = VRRP_STATE_BACK; send_group_notifies(sgroup); } void vrrp_sync_master(vrrp_t *vrrp) { vrrp_sgroup_t *sgroup = vrrp->sync; vrrp_t *isync; if (GROUP_STATE(sgroup) == VRRP_STATE_MAST) return; if (!vrrp_sync_can_goto_master(vrrp)) return; log_message(LOG_INFO, "VRRP_Group(%s) Syncing instances to MASTER state" , GROUP_NAME(sgroup)); /* Perform sync index */ list_for_each_entry(isync, &sgroup->vrrp_instances, s_list) { // TODO /* Send the higher priority advert on all synced instances */ if (isync != vrrp && isync->state != VRRP_STATE_MAST) { isync->wantstate = VRRP_STATE_MAST; // TODO 6 - transition straight to master if PRIO_OWNER // TODO 7 - not here, but generally if wantstate == MAST && !owner, ms_down_timer = adver_int + 1 skew and be backup // if (vrrp->wantstate == VRRP_STATE_MAST && vrrp->base_priority == VRRP_PRIO_OWNER) { // /* ??? */ // } else { #ifdef _WITH_SNMP_RFCV3_ isync->stats->next_master_reason = vrrp->stats->master_reason; #endif vrrp_state_goto_master(isync); vrrp_thread_requeue_read(isync); // } } } sgroup->state = VRRP_STATE_MAST; send_group_notifies(sgroup); } void vrrp_sync_fault(vrrp_t *vrrp) { vrrp_sgroup_t *sgroup = vrrp->sync; vrrp_t *isync; if (GROUP_STATE(sgroup) == VRRP_STATE_FAULT) return; log_message(LOG_INFO, "VRRP_Group(%s) Syncing instances to FAULT state" , GROUP_NAME(sgroup)); /* Perform sync index */ list_for_each_entry(isync, &sgroup->vrrp_instances, s_list) { /* We force sync instance to backup mode. * This reduce instance takeover to less than ms_down_timer. * => by default ms_down_timer is set to 3secs. * => Takeover will be less than 3secs ! */ if (isync != vrrp && isync->state != VRRP_STATE_FAULT) { isync->wantstate = VRRP_STATE_FAULT; if (isync->state == VRRP_STATE_MAST) { vrrp_state_leave_master(isync, false); } else if (isync->state == VRRP_STATE_BACK || isync->state == VRRP_STATE_INIT) { isync->state = VRRP_STATE_FAULT; /* This is a bit of a bodge */ vrrp_state_leave_fault(isync); } } } sgroup->state = VRRP_STATE_FAULT; send_group_notifies(sgroup); } keepalived-2.3.3/keepalived/vrrp/vrrp_arp.c0000664000175000017500000001610114705536371014426 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: ARP primitives. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" /* system includes */ #include #ifdef _HAVE_LINUX_IF_ETHER_H_COLLISION_ #include #endif #include #include #include #include #include /* local includes */ #include "logger.h" #include "memory.h" #include "utils.h" #include "bitops.h" #include "vrrp_scheduler.h" #include "vrrp_arp.h" /* * The size of the garp_buffer should be the large enough to hold * the largest arp packet to be sent + the size of the link layer header * for the corresponding protocol * For infiniband the link layer header consists of the destination MAC * address(20 bytes) and protocol identifier of the encapsulated * datagram(4 bytes). This is larger than the space required for Ethernet */ #define GARP_BUFFER_SIZE (sizeof(inf_arphdr_t) + sizeof (ipoib_hdr_t) +\ (INFINIBAND_ALEN)) /* static vars */ static char *garp_buffer; static int garp_fd = -1; /* Send the gratuitous ARP message */ static ssize_t send_arp(ip_address_t *ipaddress, ssize_t pack_len) { interface_t *ifp = ipaddress->ifp; struct sockaddr_storage ss; struct sockaddr_large_ll *sll = PTR_CAST(struct sockaddr_large_ll, &ss); ssize_t len; /* Build the dst device */ memset(&ss, 0, sizeof(ss)); sll->sll_family = AF_PACKET; sll->sll_hatype = ifp->hw_type; sll->sll_protocol = htons(ETHERTYPE_ARP); sll->sll_ifindex = (int) ifp->ifindex; /* The values in sll_addr and sll_halen appear to be ignored */ sll->sll_halen = ifp->hw_addr_len; memcpy(sll->sll_addr, ifp->hw_addr_bcast, ifp->hw_addr_len); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Sending gratuitous ARP on %s for %s", ifp->ifname, inet_ntop2(ipaddress->u.sin.sin_addr.s_addr)); /* Send packet */ len = sendto(garp_fd, garp_buffer, pack_len, 0, PTR_CAST(struct sockaddr, sll), sizeof(*sll)); if (len < 0) { /* coverity[bad_printf_format_string] */ log_message(LOG_INFO, "Error %d (%m) sending gratuitous ARP on %s for %s", errno, IF_NAME(ipaddress->ifp), inet_ntop2(ipaddress->u.sin.sin_addr.s_addr)); } return len; } /* Build a gratuitous ARP message over a specific interface */ ssize_t send_gratuitous_arp_immediate(interface_t *ifp, ip_address_t *ipaddress) { char *hwaddr = PTR_CAST(char, IF_HWADDR(ipaddress->ifp)); struct arphdr *arph; char *arp_ptr; ssize_t len, pack_len; if (ifp->hw_addr_len == 0) return -1; if (!garp_buffer) return -1; /* Setup link layer header */ if (ifp->hw_type == ARPHRD_INFINIBAND) { struct ipoib_hdr *ipoib; /* Add ipoib link layer header MAC + proto */ memcpy(garp_buffer, ifp->hw_addr_bcast, ifp->hw_addr_len); ipoib = PTR_CAST(struct ipoib_hdr, (garp_buffer + ifp->hw_addr_len)); ipoib->proto = htons(ETHERTYPE_ARP); ipoib->reserved = 0; arph = PTR_CAST(struct arphdr, garp_buffer + ifp->hw_addr_len + sizeof(*ipoib)); } else { struct ether_header *eth; eth = PTR_CAST(struct ether_header, garp_buffer); memcpy(eth->ether_dhost, ifp->hw_addr_bcast, ETH_ALEN < ifp->hw_addr_len ? ETH_ALEN : ifp->hw_addr_len); memcpy(eth->ether_shost, hwaddr, ETH_ALEN < ifp->hw_addr_len ? ETH_ALEN : ifp->hw_addr_len); eth->ether_type = htons(ETHERTYPE_ARP); arph = PTR_CAST(struct arphdr, (garp_buffer + ETHER_HDR_LEN)); } /* ARP payload */ arph->ar_hrd = htons(ifp->hw_type); arph->ar_pro = htons(ETHERTYPE_IP); arph->ar_hln = ifp->hw_addr_len; arph->ar_pln = sizeof(struct in_addr); arph->ar_op = htons(ARPOP_REQUEST); arp_ptr = PTR_CAST(char, (arph + 1)); memcpy(arp_ptr, hwaddr, ifp->hw_addr_len); arp_ptr += ifp->hw_addr_len; memcpy(arp_ptr, &ipaddress->u.sin.sin_addr.s_addr, sizeof(struct in_addr)); arp_ptr += sizeof (struct in_addr); memcpy(arp_ptr, ifp->hw_addr_bcast, ifp->hw_addr_len); arp_ptr += ifp->hw_addr_len; memcpy(arp_ptr, &ipaddress->u.sin.sin_addr.s_addr, sizeof(struct in_addr)); arp_ptr += sizeof(struct in_addr); pack_len = arp_ptr - garp_buffer; len = send_arp(ipaddress, pack_len); /* If we have to delay between sending garps, note the next time we can */ if (ifp->garp_delay && ifp->garp_delay->have_garp_interval) ifp->garp_delay->garp_next_time = timer_add_now(ifp->garp_delay->garp_interval); /* Cleanup room for next round */ memset(garp_buffer, 0, GARP_BUFFER_SIZE); return len; } static void queue_garp(interface_t *ifp, ip_address_t *ipaddress) { ipaddress->garp_gna_pending = 1; if (list_empty(&ifp->garp_delay->garp_list)) thread_add_timer(master, vrrp_arp_thread, ifp, timer_long(timer_sub_now(ifp->garp_delay->garp_next_time))); list_add_tail(&ipaddress->garp_gna_list, &ifp->garp_delay->garp_list); } void send_gratuitous_arp(ip_address_t *ipaddress, unsigned rep) { interface_t *ifp = IF_BASE_IFP(ipaddress->ifp); /* If the interface doesn't support ARP, don't try sending */ if (ifp->ifi_flags & IFF_NOARP) return; if (ipaddress->garp_gna_pending) { if (ipaddress->garp_gna_pending < rep) ipaddress->garp_gna_pending++; return; } set_time_now(); /* Do we need to delay sending the garp? */ if (ifp->garp_delay && ifp->garp_delay->have_garp_interval && ifp->garp_delay->garp_next_time.tv_sec && timercmp(&time_now, &ifp->garp_delay->garp_next_time, <)) queue_garp(ifp, ipaddress); else send_gratuitous_arp_immediate(ifp, ipaddress); } /* * Gratuitous ARP init/close */ bool gratuitous_arp_init(void) { if (garp_buffer) return true; /* Create the socket descriptor */ garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htons(ETH_P_ARP)); if (garp_fd < 0) { log_message(LOG_INFO, "Error %d while registering gratuitous ARP shared channel", errno); return (errno != EAFNOSUPPORT && errno != EPERM); } if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Registering gratuitous ARP shared channel"); /* We don't want to receive any data on this socket */ if_setsockopt_no_receive(&garp_fd); /* Initalize shared buffer */ garp_buffer = PTR_CAST(char, MALLOC(GARP_BUFFER_SIZE)); return true; } void gratuitous_arp_close(void) { if (garp_buffer) { FREE(garp_buffer); garp_buffer = NULL; } if (garp_fd != -1) { close(garp_fd); garp_fd = -1; } } keepalived-2.3.3/keepalived/vrrp/vrrp.c0000664000175000017500000055604414771471720013602 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: VRRP 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. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" /* System includes */ #include #ifdef _WITH_VRRP_AUTH_ #include #endif #include #include #include #ifdef _WITH_VRRP_AUTH_ #include #endif #include #include #include #include #include #if !defined ETH_HLEN || !defined ETH_ZLEN #include /* This may not be needed at all - try removing and see if any issues raised */ #endif #ifdef _NETWORK_TIMESTAMP_ #include #endif /* local include */ #include "parser.h" #include "vrrp_arp.h" #include "vrrp_ndisc.h" #include "vrrp_scheduler.h" #include "vrrp_notify.h" #include "vrrp.h" #include "global_data.h" #include "vrrp_data.h" #include "vrrp_sync.h" #include "vrrp_track.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_vmac.h" #endif #include "vrrp_if_config.h" #if defined _WITH_SNMP_RFC_ || defined _WITH_SNMP_VRRP_ #include "vrrp_snmp.h" #endif #include "list_head.h" #include "logger.h" #include "main.h" #include "utils.h" #include "bitops.h" #include "keepalived_netlink.h" #include "vrrp_iprule.h" #include "vrrp_iproute.h" #ifdef _WITH_DBUS_ #include "vrrp_dbus.h" #include "global_data.h" #endif #include "keepalived_magic.h" #include "vrrp_static_track.h" #ifdef _WITH_FIREWALL_ #include "vrrp_firewall.h" #endif #include "tracker.h" #include "track_file.h" #ifdef _WITH_TRACK_PROCESS_ #include "track_process.h" #endif #ifdef _WITH_LVS_ #include "ipvswrapper.h" #endif /* Ideally we would use a struct from a system header to determine the * size of a vlan tag, but there doesn't seem to be one exposed to * user space. */ #define VLAN_TAG_SIZE 4 /* If we don't have certain configuration, then we can optimise the * resources that keepalived uses. These are cleared by start_vrrp() * in clear_summary_flags() and set in vrrp_complete_instance() */ bool have_ipv4_instance; bool have_ipv6_instance; static bool monitor_ipv4_routes; static bool monitor_ipv6_routes; static bool monitor_ipv4_rules; static bool monitor_ipv6_rules; #ifdef _NETWORK_TIMESTAMP_ bool do_network_timestamp; #endif #ifdef _CHECKSUM_DEBUG_ bool do_checksum_debug; #endif static void vrrp_notify_fifo_script_exit(__attribute__((unused)) thread_ref_t thread) { log_message(LOG_INFO, "vrrp notify fifo script terminated"); } void clear_summary_flags(void) { have_ipv4_instance = false; have_ipv6_instance = false; monitor_ipv4_routes = false; monitor_ipv6_routes = false; monitor_ipv4_rules = false; monitor_ipv6_rules = false; } /* add/remove Virtual IP addresses */ static bool vrrp_handle_ipaddress(vrrp_t *vrrp, int cmd, int type, bool force) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) %sing %sVIPs.", vrrp->iname, (cmd == IPADDRESS_ADD) ? "sett" : "remov", (type == VRRP_VIP_TYPE) ? "" : "E-"); return netlink_iplist((type == VRRP_VIP_TYPE) ? &vrrp->vip : &vrrp->evip, cmd, force); } /* add/remove Virtual routes */ static void vrrp_handle_iproutes(vrrp_t * vrrp, int cmd, bool force) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) %sing Virtual Routes", vrrp->iname, (cmd == IPROUTE_ADD) ? "sett" : "remov"); netlink_rtlist(&vrrp->vroutes, cmd, force); } /* add/remove Virtual rules */ static void vrrp_handle_iprules(vrrp_t * vrrp, int cmd, bool force) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) %sing Virtual Rules", vrrp->iname, (cmd == IPRULE_ADD) ? "sett" : "remov"); netlink_rulelist(&vrrp->vrules, cmd, force); } #ifdef _WITH_FIREWALL_ static void vrrp_handle_accept_mode(vrrp_t *vrrp, int cmd, bool force) { if (vrrp->base_priority == VRRP_PRIO_OWNER || vrrp->accept) return; if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) %s%s", vrrp->iname, (cmd == IPADDRESS_ADD) ? "sett" : "remov", "ing firewall drop rule"); firewall_handle_accept_mode(vrrp, cmd, force); } #endif /* Check that the scripts are secure */ static unsigned check_track_script_secure(vrrp_script_t *script, magic_t magic) { unsigned flags; if (script->insecure) return 0; flags = check_script_secure(&script->script, magic); /* Mark not to run if needs inhibiting */ if (flags & SC_INHIBIT) { report_config_error(CONFIG_GENERAL_ERROR, "Disabling track script %s due to insecure", script->sname); script->insecure = true; } else if (flags & SC_NOTFOUND) { report_config_error(CONFIG_GENERAL_ERROR, "Disabling track script %s since not found/accessible", script->sname); script->insecure = true; } else if (!(flags & (SC_EXECUTABLE | SC_SYSTEM))) script->insecure = true; return flags; } static void check_vrrp_script_security(void) { vrrp_t *vrrp; vrrp_sgroup_t *sg; tracked_sc_t *track_script, *track_script_tmp; vrrp_script_t *vscript, *vscript_tmp; unsigned script_flags = 0; magic_t magic; if (list_empty(&vrrp_data->vrrp)) return; magic = ka_magic_open(); /* Set the insecure flag of any insecure scripts */ list_for_each_entry(vscript, &vrrp_data->vrrp_script, e_list) script_flags |= check_track_script_secure(vscript, magic); list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { script_flags |= check_notify_script_secure(&vrrp->script_backup, magic); script_flags |= check_notify_script_secure(&vrrp->script_master, magic); script_flags |= check_notify_script_secure(&vrrp->script_fault, magic); script_flags |= check_notify_script_secure(&vrrp->script_stop, magic); script_flags |= check_notify_script_secure(&vrrp->script_deleted, magic); script_flags |= check_notify_script_secure(&vrrp->script, magic); script_flags |= check_notify_script_secure(&vrrp->script_master_rx_lower_pri, magic); list_for_each_entry_safe(track_script, track_script_tmp, &vrrp->track_script, e_list) { if (track_script->scr->insecure) { /* Remove it from the vrrp instance's queue */ free_track_script(track_script); } } } list_for_each_entry(sg, &vrrp_data->vrrp_sync_group, e_list) { script_flags |= check_notify_script_secure(&sg->script_backup, magic); script_flags |= check_notify_script_secure(&sg->script_master, magic); script_flags |= check_notify_script_secure(&sg->script_fault, magic); script_flags |= check_notify_script_secure(&sg->script_stop, magic); script_flags |= check_notify_script_secure(&sg->script, magic); list_for_each_entry_safe(track_script, track_script_tmp, &sg->track_script, e_list) { if (track_script->scr->insecure) { /* Remove it from the vrrp sync group's queue */ free_track_script(track_script); } } } if (global_data->notify_fifo.script) script_flags |= check_notify_script_secure(&global_data->notify_fifo.script, magic); if (global_data->vrrp_notify_fifo.script) script_flags |= check_notify_script_secure(&global_data->vrrp_notify_fifo.script, magic); if (!script_security && script_flags & SC_ISSCRIPT) { report_config_error(CONFIG_SECURITY_ERROR, "SECURITY VIOLATION - scripts are being executed but script_security not enabled.%s", script_flags & SC_INSECURE ? " There are insecure scripts." : ""); } if (magic) ka_magic_close(magic); /* Now walk through the vrrp_script list, removing any that aren't used */ list_for_each_entry_safe(vscript, vscript_tmp, &vrrp_data->vrrp_script, e_list) { if (vscript->insecure) { free_vscript(vscript); } } } /* VRRP header length */ static size_t vrrp_pkt_len(const vrrp_t *vrrp) { size_t len = sizeof(vrrphdr_t); if (vrrp->family == AF_INET) { if (vrrp->version == VRRP_VERSION_2) len += VRRP_AUTH_LEN; len += ((!list_empty(&vrrp->vip)) ? vrrp->vip_cnt * sizeof(struct in_addr) : 0); } else if (vrrp->family == AF_INET6) len += ((!list_empty(&vrrp->vip)) ? vrrp->vip_cnt * sizeof(struct in6_addr) : 0); return len; } size_t __attribute__ ((pure)) vrrp_adv_len(const vrrp_t *vrrp) { size_t len = vrrp_pkt_len(vrrp); if (vrrp->family == AF_INET) { len += sizeof(struct iphdr); #ifdef _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) len += sizeof(ipsec_ah_t); #endif } return len; } /* VRRP header pointer from buffer */ const vrrphdr_t * vrrp_get_header(sa_family_t family, const char *buf, size_t len) { const struct iphdr *iph; /* Since the raw sockets only specify IPPROTO_VRRP or (for IPv4) * IPPROTO_AH, it is safe to assume IPPROTO_VRRP if it is not * IPv4 and IPPROTO_AH. */ if (family == AF_INET) { iph = PTR_CAST_CONST(struct iphdr, buf); /* Ensure we have received the full vrrp header */ if (len < sizeof(struct iphdr) || len < (iph->ihl << 2) + sizeof(vrrphdr_t)) { log_message(LOG_INFO, "IPv4 VRRP packet too short - %zu bytes", len); return NULL; } /* Fill the VRRP header */ #ifdef _WITH_VRRP_AUTH_ if (iph->protocol == IPPROTO_AH) { /* Make sure we have received the full vrrp header */ if (len < (iph->ihl << 2) + sizeof(ipsec_ah_t) + sizeof(vrrphdr_t)) { log_message(LOG_INFO, "IPv4 VRRP packet with AH too short - %zu bytes", len); return NULL; } return PTR_CAST_CONST(vrrphdr_t, (const char *)iph + (iph->ihl << 2) + sizeof(ipsec_ah_t)); } #endif return PTR_CAST_CONST(vrrphdr_t, (const char *)iph + (iph->ihl << 2)); } if (family == AF_INET6) { /* Make sure we have received the full vrrp header */ if (len < sizeof(vrrphdr_t)) { log_message(LOG_INFO, "IPv6 VRRP packet too short - %zu bytes", len); return NULL; } return PTR_CAST_CONST(vrrphdr_t, buf); } return NULL; } static size_t expected_vrrp_pkt_len(const vrrphdr_t *vh, int family) { size_t len = sizeof(vrrphdr_t); if (family == AF_INET) { if (vh->vers_type >> 4 == VRRP_VERSION_2) len += VRRP_AUTH_LEN; len += vh->naddr * sizeof(struct in_addr); } else if (family == AF_INET6) len += vh->naddr * sizeof(struct in6_addr); return len; } static void vrrp_update_pkt(vrrp_t *vrrp, uint8_t prio, sockaddr_t *addr) { char *bufptr = vrrp->send_buffer; vrrphdr_t *hd; #ifdef _WITH_VRRP_AUTH_ bool final_update; unicast_peer_t *peer = NULL; #endif uint32_t new_saddr = 0; uint32_t new_daddr; #ifdef _WITH_VRRP_AUTH_ /* We will need to be called again if there is more than one unicast peer, so don't calculate checksums */ if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) peer = list_first_entry(&vrrp->unicast_peer, unicast_peer_t, e_list); final_update = (!peer || list_is_last(&peer->e_list, &vrrp->unicast_peer) || addr); #endif if (vrrp->family == AF_INET) { bufptr += sizeof(struct iphdr); #ifdef _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) bufptr += sizeof(ipsec_ah_t); #endif } hd = PTR_CAST(vrrphdr_t, bufptr); if (hd->priority != prio) { if (vrrp->family == AF_INET) { /* HC' = ~(~HC + ~m + m') */ uint16_t *prio_addr = PTR_CAST(uint16_t, ((char *)&hd->priority - (((char *)hd -(char *)&hd->priority) & 1))); uint16_t old_val = *prio_addr; hd->priority = prio; hd->chksum = csum_incremental_update16(hd->chksum, old_val, *prio_addr); } else hd->priority = prio; } if (vrrp->family == AF_INET) { struct iphdr *ip = PTR_CAST(struct iphdr, (vrrp->send_buffer)); if (!addr) { /* kernel will fill in ID if left to 0, so we overflow to 1 */ if (!++vrrp->ip_id) ++vrrp->ip_id; ip->id = htons(vrrp->ip_id); } else { /* If unicast address */ if (vrrp->version == VRRP_VERSION_2) ip->daddr = inet_sockaddrip4(addr); else { new_daddr = inet_sockaddrip4(addr); if (ip->daddr != new_daddr) { #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ if (vrrp->unicast_chksum_compat < CHKSUM_COMPATIBILITY_MIN_COMPAT) #endif hd->chksum = csum_incremental_update32(hd->chksum, ip->daddr, new_daddr); ip->daddr = new_daddr; } } } /* Has the source address changed? */ if (!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags) && ip->saddr != PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr.s_addr) { if (vrrp->version == VRRP_VERSION_2) ip->saddr = PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr.s_addr; else { new_saddr = PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr.s_addr; hd->chksum = csum_incremental_update32(hd->chksum, ip->saddr, new_saddr); ip->saddr = new_saddr; } } #ifdef _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) { unsigned char digest[MD5_DIGEST_LENGTH]; ipsec_ah_t *ah = PTR_CAST(ipsec_ah_t, (vrrp->send_buffer + sizeof (struct iphdr))); if (new_saddr) ah->spi = new_saddr; if (!addr) { /* Processing sequence number. Cycled assumed if 0xFFFFFFFD reached. So the MASTER state is free for another srv. Here can result a flapping MASTER state owner when max seq_number value reached. => We REALLY REALLY REALLY don't need to worry about this. We only use authentication for VRRPv2, for which the adver_int is specified in whole seconds, therefore the minimum adver_int is 1 second. 2^32-3 seconds is 4294967293 seconds, or in excess of 136 years, so since the sequence number always starts from 0, we are not going to reach the limit. In the current implementation if counter has cycled, we stop sending adverts and become BACKUP. We are ever the optimist and think we might run continuously for over 136 years without someone redesigning their network! If all the master are down we reset the counter for becoming MASTER. */ if (vrrp->ipsecah_counter.seq_number > 0xFFFFFFFD) { vrrp->ipsecah_counter.cycle = true; } else { vrrp->ipsecah_counter.seq_number++; } ah->seq_number = htonl(vrrp->ipsecah_counter.seq_number); } if (final_update) { struct iphdr iph = *ip; /* zero the ip mutable fields */ iph.tos = 0; iph.frag_off = 0; if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) iph.ttl = 0; /* Compute the ICV & trunc the digest to 96bits => No padding needed. -- rfc2402.3.3.3.1.1.1 & rfc2401.5 */ memset(&ah->auth_data, 0, sizeof(ah->auth_data)); hmac_md5(PTR_CAST_CONST(unsigned char, &iph), sizeof iph, PTR_CAST_CONST(unsigned char, ah), vrrp->send_buffer_size - sizeof(struct iphdr), vrrp->auth_data, sizeof(vrrp->auth_data), digest); memcpy(ah->auth_data, digest, HMAC_MD5_TRUNC); } } #endif } } #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ static void vrrp_csum_mcast(vrrp_t *vrrp) { char *bufptr = vrrp->send_buffer; vrrphdr_t *hd; bufptr += sizeof(struct iphdr); #ifdef _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) bufptr += sizeof(ipsec_ah_t); #endif hd = PTR_CAST(vrrphdr_t, bufptr); struct iphdr *ip = PTR_CAST(struct iphdr, (vrrp->send_buffer)); if (vrrp->unicast_chksum_compat == CHKSUM_COMPATIBILITY_AUTO && ip->daddr != global_data->vrrp_mcast_group4.sin_addr.s_addr) { /* The checksum is calculated using the standard multicast address */ hd->chksum = csum_incremental_update32(hd->chksum, ip->daddr, global_data->vrrp_mcast_group4.sin_addr.s_addr); } } #endif #ifdef _WITH_VRRP_AUTH_ /* * IPSEC AH incoming packet check. * return false for a valid pkt, true otherwise. */ static bool vrrp_in_chk_ipsecah(vrrp_t *vrrp, const struct iphdr *ip, const ipsec_ah_t *ah, const vrrphdr_t *hd, size_t buflen) { size_t hdr_len = (const char *)ah - (const char *)ip; unsigned char digest[MD5_DIGEST_LENGTH]; unsigned char tmp_buf[(15 << 2) + sizeof(ipsec_ah_t)] __attribute__((aligned(__alignof__(struct iphdr)))); /* Allow for max ip header size */ struct iphdr *ip_tmp = PTR_CAST(struct iphdr, tmp_buf); ipsec_ah_t *ah_tmp = PTR_CAST(ipsec_ah_t, ((char *)ip_tmp + hdr_len)); /* * First compute an ICV to compare with the one present in AH pkt. * If they don't match, we can't consider any fields in the received * packet to be valid. */ hdr_len = (const char *)hd - (const char *)ip; /* zero the ip mutable fields */ memcpy(tmp_buf, ip, hdr_len); ip_tmp->tos = 0; ip_tmp->frag_off = 0; ip_tmp->check = 0; if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) ip_tmp->ttl = 0; memset(ah_tmp->auth_data, 0, sizeof (ah_tmp->auth_data)); memset(digest, 0, MD5_DIGEST_LENGTH); /* Compute the ICV */ hmac_md5((const unsigned char *)ip_tmp, hdr_len, (const unsigned char *)hd, buflen - ((const unsigned char *)hd - (const unsigned char *)ip) , vrrp->auth_data, sizeof (vrrp->auth_data) , digest); if (memcmp_constant_time(ah->auth_data, digest, HMAC_MD5_TRUNC) != 0) { log_message(LOG_INFO, "(%s) IPSEC-AH : invalid" " IPSEC HMAC-MD5 value. Due to fields mutation" " or bad password !", vrrp->iname); return true; } /* Now verify that the SPI value is equal to src IP */ if (ah->spi != ip->saddr) { log_message(LOG_INFO, "IPSEC AH : invalid IPSEC SPI value. %u and expect %u", ip->saddr, ah->spi); return true; } // TODO - If SPI doesn't match previous SPI, we are starting again /* * then proceed with the sequence number to prevent against replay attack. */ if (ntohl(ah->seq_number) > vrrp->ipsecah_counter.seq_number) vrrp->ipsecah_counter.seq_number = ntohl(ah->seq_number); else { log_message(LOG_INFO, "(%s) IPSEC-AH : sequence number %u" " already processed. Packet dropped. Local(%" PRIu32 ")", vrrp->iname, ntohl(ah->seq_number), vrrp->ipsecah_counter.seq_number); return true; } return false; } #endif /* check if ipaddr is present in VIP buffer */ static bool __attribute__((pure)) vrrp_in_chk_vips(const vrrp_t *vrrp, const ip_address_t *ipaddress, const void *buffer, unsigned naddr) { size_t i; const struct in_addr *addr4_buf; const struct in6_addr *addr6_buf; if (vrrp->family == AF_INET) { addr4_buf = buffer; for (i = 0; i < naddr; i++) { if (!memcmp(&ipaddress->u.sin.sin_addr.s_addr, &addr4_buf[i], sizeof (struct in_addr))) return true; } } else if (vrrp->family == AF_INET6) { addr6_buf = buffer; for (i = 0; i < naddr; i++) { if (!memcmp(&ipaddress->u.sin6_addr, &addr6_buf[i], sizeof (struct in6_addr))) return true; } } return false; } #ifdef _CHECKSUM_DEBUG_ static void check_tx_checksum(vrrp_t *vrrp, unicast_peer_t *peer) { struct iphdr *ip = PTR_CAST(struct iphdr, vrrp->send_buffer); vrrphdr_t *hd = PTR_CAST(vrrphdr_t, ((char *)vrrp->send_buffer + sizeof(struct iphdr))); size_t vrrppkt_len; uint32_t acc_csum; ipv4_phdr_t ipv4_phdr; uint16_t calc_chksum; uint16_t pkt_chksum; checksum_check_t *chk = peer ? &peer->chk : &vrrp->chk; #ifdef _WITH_VRRP_AUTH_ if (ip->protocol == IPPROTO_AH) hd = PTR_CAST(vrrphdr_t, ((char *)hd + sizeof(ipsec_ah_t))); #endif vrrppkt_len = sizeof(vrrphdr_t) + hd->naddr * sizeof(struct in_addr); if (vrrp->version == VRRP_VERSION_3) { if (__test_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, &vrrp->flags)) acc_csum = 0; else { /* Create IPv4 pseudo-header */ ipv4_phdr.src = ip->saddr; #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ ipv4_phdr.dst = vrrp->unicast_chksum_compat <= CHKSUM_COMPATIBILITY_MIN_COMPAT ? ip->daddr : global_data->vrrp_mcast_group4.sin_addr.s_addr; #else ipv4_phdr.dst = ip->daddr; #endif ipv4_phdr.zero = 0; ipv4_phdr.proto = IPPROTO_VRRP; ipv4_phdr.len = htons(vrrppkt_len); in_csum(PTR_CAST_CONST(void, &ipv4_phdr), sizeof(ipv4_phdr), 0, &acc_csum); } } else { vrrppkt_len += VRRP_AUTH_LEN; acc_csum = 0; } pkt_chksum = hd->chksum; hd->chksum = 0; calc_chksum = in_csum(PTR_CAST_CONST(void, hd), vrrppkt_len, acc_csum, &acc_csum); hd->chksum = pkt_chksum; if (calc_chksum != pkt_chksum || !chk->sent_to || acc_csum != chk->last_tx_checksum) { sockaddr_t *dst_addr; sockaddr_t addr; if (peer) dst_addr = &peer->address; else { inet_ip4tosockaddr(&global_data->vrrp_mcast_group4.sin_addr, &addr); dst_addr = &addr; } if (!chk->sent_to) log_message(LOG_INFO, "(%s): First advert to %s, checksum: pkt 0x%4.4x, calc 0x%4.4x acc 0x%x%s", vrrp->iname, inet_sockaddrtos(dst_addr), pkt_chksum, calc_chksum, acc_csum, pkt_chksum != calc_chksum ? " - MISMATCH" : ""); else if (hd->priority != chk->last_tx_priority && acc_csum - htons(hd->priority << 8) == (chk->last_tx_checksum - htons(chk->last_tx_priority << 8))) log_message(LOG_INFO, "(%s): Checksum change to %s (priority %d to %d), checksum: pkt 0x%4.4x, calc 0x%4.4x acc 0x%x, previous acc 0x%x", vrrp->iname, inet_sockaddrtos(dst_addr), chk->last_tx_priority, hd->priority, pkt_chksum, calc_chksum, acc_csum, chk->last_tx_checksum); else if (pkt_chksum != hd->chksum || acc_csum != chk->last_tx_checksum) log_message(LOG_INFO, "(%s): Checksum ERROR to %s, checksum: pkt 0x%4.4x, calc 0x%4.4x acc 0x%x, previous acc 0x%x", vrrp->iname, inet_sockaddrtos(dst_addr), pkt_chksum, calc_chksum, acc_csum, chk->last_tx_checksum); if (vrrp->version == VRRP_VERSION_3) log_buffer("IPv4 pseudo header", &ipv4_phdr, sizeof ipv4_phdr); log_buffer("Advert packet", vrrp->send_buffer, vrrp->send_buffer_size); chk->sent_to = true; chk->last_tx_checksum = acc_csum; chk->last_tx_priority = hd->priority; } } static void check_rx_checksum(vrrp_t *vrrp, const ipv4_phdr_t *ipv4_phdr, const struct iphdr *iph, size_t pkt_len, const vrrphdr_t *vrrp_pkt, uint16_t calc_chksum, uint32_t acc_csum) { unicast_peer_t *peer; struct in_addr *saddr4; sockaddr_t addr; checksum_check_t *chk; bool peer_found = false; /* If unicast, find the sending peer */ saddr4 = &PTR_CAST(struct sockaddr_in, &vrrp->pkt_saddr)->sin_addr; list_for_each_entry(peer, &vrrp->unicast_peer, e_list) { peer_found = true; if (saddr4->s_addr == PTR_CAST(struct sockaddr_in, &peer->address)->sin_addr.s_addr) { break; } } chk = peer_found ? &peer->chk : &vrrp->chk; if (calc_chksum || !chk->received_from || chk->last_rx_checksum != vrrp_pkt->chksum || chk->last_rx_from != saddr4->s_addr || chk->last_rx_priority != vrrp_pkt->priority) { inet_ip4tosockaddr(saddr4, &addr); if (!chk->received_from) log_message(LOG_INFO, "%s: First received advert from %s, checksum: pkt 0x%4.4x, calc 0x%4.4x, acc 0x%x%s", vrrp->iname, inet_sockaddrtos(&addr), vrrp_pkt->chksum, calc_chksum, acc_csum, calc_chksum ? " - MISMATCH" : ""); else if (calc_chksum) log_message(LOG_INFO, "(%s): Checksum ERROR from %s, checksum: pkt 0x%4.4x, previous 0x%4.4x, calc 0x%4.4x acc 0x%x", vrrp->iname, inet_sockaddrtos(&addr), vrrp_pkt->chksum, chk->last_rx_checksum, calc_chksum, acc_csum); else if (chk->last_rx_from != saddr4->s_addr) { char old_addr[INET_ADDRSTRLEN]; log_message(LOG_INFO, "(%s): Checksum valid change from %s (was %s), checksum: pkt 0x%4.4x, previous 0x%4.4x calc 0x%4.4x acc 0x%x", vrrp->iname, inet_sockaddrtos(&addr), inet_ntop(AF_INET, &chk->last_rx_from, old_addr, sizeof(old_addr)), vrrp_pkt->chksum, chk->last_rx_checksum, calc_chksum, acc_csum); } else if (chk->last_rx_priority != vrrp_pkt->priority) log_message(LOG_INFO, "(%s): Checksum valid change from %s (priority %d to %d), checksum: pkt 0x%4.4x, previous 0x%4.4x, calc 0x%4.4x acc 0x%x", vrrp->iname, inet_sockaddrtos(&addr), chk->last_rx_priority, vrrp_pkt->priority, chk->last_rx_checksum, vrrp_pkt->chksum, calc_chksum, acc_csum); else log_message(LOG_INFO, "(%s): Checksum valid change from %s, checksum: 0x%4.4x, previous 0x%4.4x, acc 0x%x", vrrp->iname, inet_sockaddrtos(&addr), vrrp_pkt->chksum, chk->last_rx_checksum, acc_csum); if (ipv4_phdr) log_buffer("IPv4 pseudo header", ipv4_phdr, sizeof(*ipv4_phdr)); log_buffer("Advert packet", iph, pkt_len); chk->received_from = true; chk->last_rx_checksum = vrrp_pkt->chksum; chk->last_rx_priority = vrrp_pkt->priority; chk->last_rx_from = saddr4->s_addr; } } #endif static void __attribute__ ((format (printf, 3, 4))) log_rate_limited_error(vrrp_t *vrrp, vrrp_rlflags_t rlflag, const char *format, ...) { va_list args; /* If this error has already been logged, skip message */ if (vrrp->rlflags & rlflag) return; /* Record that this error has been logged */ vrrp->rlflags |= rlflag; va_start(args, format); vlog_message(LOG_INFO, format, args); va_end(args); } static inline bool check_ttl_hl(vrrp_t *vrrp, const unicast_peer_t *up_addr) { if (vrrp->rx_ttl_hl != -1 && (vrrp->rx_ttl_hl < up_addr->min_ttl || vrrp->rx_ttl_hl > up_addr->max_ttl)) { ++vrrp->stats->ip_ttl_err; #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->proto_err_reason = ipTtlError; vrrp_rfcv3_snmp_proto_err_notify(vrrp); #endif log_rate_limited_error(vrrp, VRRP_RLFLAG_TTL_NOT_IN_RANGE, "(%s) TTL/HL %d from %s not in range [%d, %d]", vrrp->iname, vrrp->rx_ttl_hl, inet_sockaddrtos(&vrrp->pkt_saddr), up_addr->min_ttl, up_addr->max_ttl); return false; } return true; } /* * VRRP incoming packet check. * return VRRP_PACKET_OK if the pkt is valid, or * VRRP_PACKET_KO if packet invalid or * VRRP_PACKET_DROP if packet not relevant to us * VRRP_PACKET_OTHER if packet has wrong vrid * * Note: If we return anything other that VRRP_PACKET_OK, we should log the reason why * * On entry, we have already checked that sufficient data has been received for the * IP header (if IPv4), the ipsec_ah header (if IPv4 and the ip header protocol * is IPPROTO_AH), and the VRRP protocol header. We haven't yet checked that there is * suficient data received for all the VIPs. */ static int vrrp_check_packet(vrrp_t *vrrp, const vrrphdr_t *hd, const char *buffer, ssize_t buflen_ret, bool check_vip_addr) { const struct iphdr *ip = PTR_CAST_CONST(struct iphdr, buffer); /* Stop coverity issuing NULL pointer dereference warning */ int ihl = 0; /* Stop compiler issuing possibly uninitialised warning */ size_t vrrppkt_len; #ifdef _WITH_VRRP_AUTH_ const ipsec_ah_t *ah; #endif const void *vips; ip_address_t *ipaddress; char addr_str[INET6_ADDRSTRLEN]; ipv4_phdr_t ipv4_phdr; uint32_t acc_csum = 0; unicast_peer_t *up_addr = NULL; size_t buflen, expected_len; #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ bool chksum_error; #endif uint16_t csum_calc; buflen = (size_t)buflen_ret; /* IPv4 related */ if (vrrp->family == AF_INET) { /* To begin with, we just concern ourselves with the protocol headers */ ihl = ip->ihl << 2; expected_len = ihl; #ifdef _WITH_VRRP_AUTH_ /* Check we have an AH header if expect AH, and don't have it if not */ if ((ip->protocol == IPPROTO_AH) != (vrrp->auth_type == VRRP_AUTH_AH)) { if (ip->protocol == IPPROTO_AH) log_rate_limited_error(vrrp, VRRP_RLFLAG_BAD_AH_HEADER, "(%s) Received AH header but auth type not AH from %s", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); else log_rate_limited_error(vrrp, VRRP_RLFLAG_BAD_AH_HEADER, "(%s) No AH header but auth type is AH from %s", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); ++vrrp->stats->authtype_mismatch; #ifdef _WITH_SNMP_RFCV2_ vrrp_rfcv2_snmp_auth_err_trap(vrrp, PTR_CAST(struct sockaddr_in, &vrrp->pkt_saddr)->sin_addr, authTypeMismatch); #endif return VRRP_PACKET_KO; } if (vrrp->auth_type == VRRP_AUTH_AH) expected_len += sizeof(ipsec_ah_t); #endif } else if (vrrp->family == AF_INET6) { expected_len = 0; } else { log_rate_limited_error(vrrp, VRRP_RLFLAG_BAD_IP_VERSION, "(%s) configured address family is %d, which is neither AF_INET or AF_INET6. This is probably a bug - please report", vrrp->iname, vrrp->family); return VRRP_PACKET_KO; } /* Now calculate expected_len to include everything */ expected_len += expected_vrrp_pkt_len(hd, vrrp->family); /* * MUST verify that the received packet contains the complete VRRP * packet (including fixed fields, and IPvX address(es)). */ if (buflen != expected_len) { /* Allow for Ethernet frame padding. If there is padding, the * frame length (excluding FCS) is 60 octets (ETH_ZLEN). * The Ethernet header (14 bytes - ETH_HLEN) and any Vlan * headers (4 bytes each) are removed before we receive the * packet. * Padding added is ETH_ZLEN - ETH_HLEN - expected_len, or * multiples of 4 (Vlan header) less than that. Checking the * amount of padding added can therefore only be done modulo 4. */ if (expected_len < ETH_ZLEN - ETH_HLEN && expected_len < buflen && (buflen - expected_len) % VLAN_TAG_SIZE == (VLAN_TAG_SIZE - (ETH_ZLEN - ETH_HLEN) % VLAN_TAG_SIZE) % VLAN_TAG_SIZE) { /* This is OK, there is some padding */ } else { log_rate_limited_error(vrrp, VRRP_RLFLAG_INCOMPLETE_PACKET, "(%s) vrrp packet from %s too %s, length %zu and expect %zu", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), buflen > expected_len ? "long" : "short", buflen, expected_len); ++vrrp->stats->packet_len_err; return VRRP_PACKET_KO; } } /* MUST verify that the IPv4 TTL/IPv6 HL is 255 (but not if unicast) */ if (!__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) && vrrp->rx_ttl_hl != -1 && vrrp->rx_ttl_hl != VRRP_IP_TTL) { log_rate_limited_error(vrrp, VRRP_RLFLAG_INVALID_TTL, "(%s) invalid TTL/HL from %s. Received %d and expect %d", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), vrrp->rx_ttl_hl, VRRP_IP_TTL); ++vrrp->stats->ip_ttl_err; #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->proto_err_reason = ipTtlError; vrrp_rfcv3_snmp_proto_err_notify(vrrp); #endif return VRRP_PACKET_KO; } /* MUST verify the VRRP version */ if ((hd->vers_type >> 4) != vrrp->version) { log_rate_limited_error(vrrp, VRRP_RLFLAG_WRONG_VERSION, "(%s) wrong VRRP version from %s. Received %d and expect %d", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), (hd->vers_type >> 4), vrrp->version); #ifdef _WITH_SNMP_RFC_ vrrp->stats->vers_err++; #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->proto_err_reason = versionError; vrrp_rfcv3_snmp_proto_err_notify(vrrp); #endif #endif return VRRP_PACKET_KO; } if (vrrp->version == VRRP_VERSION_2) { /* Check that authentication of packet is correct */ if ( #ifdef _WITH_VRRP_AUTH_ hd->v2.auth_type != VRRP_AUTH_AH && hd->v2.auth_type != VRRP_AUTH_PASS && #endif hd->v2.auth_type != VRRP_AUTH_NONE) { log_rate_limited_error(vrrp, VRRP_RLFLAG_BAD_AUTH, "(%s) Invalid auth type from %s: %d", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), hd->v2.auth_type); ++vrrp->stats->invalid_authtype; #ifdef _WITH_SNMP_RFCV2_ vrrp_rfcv2_snmp_auth_err_trap(vrrp, PTR_CAST(struct sockaddr_in, &vrrp->pkt_saddr)->sin_addr, invalidAuthType); #endif return VRRP_PACKET_KO; } #ifdef _WITH_VRRP_AUTH_ /* * MUST perform authentication specified by Auth Type * check the authentication type */ if (vrrp->auth_type != hd->v2.auth_type) { log_rate_limited_error(vrrp, VRRP_RLFLAG_WRONG_AUTH, "(%s) received a %d auth from %s, expecting %d!", vrrp->iname, hd->v2.auth_type, inet_sockaddrtos(&vrrp->pkt_saddr), vrrp->auth_type); ++vrrp->stats->authtype_mismatch; #ifdef _WITH_SNMP_RFCV2_ vrrp_rfcv2_snmp_auth_err_trap(vrrp, PTR_CAST(struct sockaddr_in, &vrrp->pkt_saddr)->sin_addr, authTypeMismatch); #endif return VRRP_PACKET_KO; } if (vrrp->auth_type == VRRP_AUTH_PASS) { /* check the authentication if it is a passwd */ const char *pw = (const char *)ip + ntohs(ip->tot_len) - sizeof (vrrp->auth_data); if (memcmp_constant_time(pw, vrrp->auth_data, sizeof(vrrp->auth_data)) != 0) { log_rate_limited_error(vrrp, VRRP_RLFLAG_WRONG_AUTH_PASSWD, "(%s) received an invalid passwd from %s!", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); ++vrrp->stats->auth_failure; #ifdef _WITH_SNMP_RFCV2_ vrrp_rfcv2_snmp_auth_err_trap(vrrp, PTR_CAST(struct sockaddr_in, &vrrp->pkt_saddr)->sin_addr, authFailure); #endif return VRRP_PACKET_KO; } } else if (vrrp->auth_type == VRRP_AUTH_AH) { ah = PTR_CAST_CONST(ipsec_ah_t, buffer + ihl); /* Check that the next header is vrrphdr_t */ if (ah->next_header != IPPROTO_VRRP) { /* This is an AH header for some other protocol - ignore packet */ return VRRP_PACKET_DROP; } /* check the authentication if it is ipsec ah */ if (vrrp_in_chk_ipsecah(vrrp, ip, ah, hd, buflen)) { ++vrrp->stats->auth_failure; #ifdef _WITH_SNMP_RFCV2_ vrrp_rfcv2_snmp_auth_err_trap(vrrp, PTR_CAST(struct sockaddr_in, &vrrp->pkt_saddr)->sin_addr, authFailure); #endif return VRRP_PACKET_KO; } if (vrrp->state == VRRP_STATE_BACK && ntohl(ah->seq_number) >= vrrp->ipsecah_counter.seq_number) vrrp->ipsecah_counter.cycle = false; } #endif /* * MUST verify that the Adver Interval in the packet is the same as * the locally configured for this virtual router if VRRPv2 */ if (vrrp->adver_int != hd->v2.adver_int * TIMER_HZ) { log_rate_limited_error(vrrp, VRRP_RLFLAG_ADV_INTVL_MISMATCH, "(%s) advertisement interval mismatch with %s mine=%u sec rcv'd=%d sec", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), vrrp->adver_int / TIMER_HZ, hd->v2.adver_int); /* to prevent concurent VRID running => multiple master in 1 VRID */ return VRRP_PACKET_DROP; } } /* verify packet type */ if ((hd->vers_type & 0x0f) != VRRP_PKT_ADVERT) { log_rate_limited_error(vrrp, VRRP_RLFLAG_NOT_ADVERTISEMENT, "(%s) Invalid packet type from %s. %d and expect %d", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), (hd->vers_type & 0x0f), VRRP_PKT_ADVERT); ++vrrp->stats->invalid_type_rcvd; return VRRP_PACKET_KO; } /* Check the IP header total packet length matches what we received */ if (vrrp->family == AF_INET && ntohs(ip->tot_len) != buflen) { /* Allow for Ethernet frame padding. See earlier comment * for details. */ if (buflen <= ETH_ZLEN - ETH_HLEN && ntohs(ip->tot_len) < buflen && (buflen - ntohs(ip->tot_len)) % VLAN_TAG_SIZE == (VLAN_TAG_SIZE - (ETH_ZLEN - ETH_HLEN) % VLAN_TAG_SIZE) % VLAN_TAG_SIZE) { /* This is OK, there is some padding */ } else { log_rate_limited_error(vrrp, VRRP_RLFLAG_BAD_LENGTH, "(%s) ip_tot_len mismatch against received length from %s. %d and received %zu", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), ntohs(ip->tot_len), buflen); ++vrrp->stats->packet_len_err; return VRRP_PACKET_KO; } } if (vrrp->version == VRRP_VERSION_3) { /* VRRP version 3. SHOULD check advert intervals match */ if (!(vrrp->rlflags & VRRP_RLFLAG_ADV_INTVL_MISMATCH) && vrrp->adver_int != (V3_PKT_ADVER_INT_NTOH(hd->v3.adver_int)) * TIMER_CENTI_HZ) { log_rate_limited_error(vrrp, VRRP_RLFLAG_ADV_INTVL_MISMATCH, "(%s) advertisement interval mismatch ours = %u centi-sec rcv'd=%d centi-sec", vrrp->iname, vrrp->adver_int / TIMER_CENTI_HZ, V3_PKT_ADVER_INT_NTOH(hd->v3.adver_int)); } } /* MUST verify the VRRP checksum. Kernel takes care of checksum mismatch incase of IPv6. */ if (vrrp->family == AF_INET) { vrrppkt_len = sizeof(vrrphdr_t) + hd->naddr * sizeof(struct in_addr); if (vrrp->version == VRRP_VERSION_3) { if (__test_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, &vrrp->flags)) acc_csum = 0; else { /* Create IPv4 pseudo-header */ ipv4_phdr.src = ip->saddr; #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ ipv4_phdr.dst = vrrp->unicast_chksum_compat <= CHKSUM_COMPATIBILITY_MIN_COMPAT ? ip->daddr : global_data->vrrp_mcast_group4.sin_addr.s_addr; #else ipv4_phdr.dst = ip->daddr; #endif ipv4_phdr.zero = 0; ipv4_phdr.proto = IPPROTO_VRRP; ipv4_phdr.len = htons(vrrppkt_len); in_csum(PTR_CAST_CONST(void, &ipv4_phdr), sizeof(ipv4_phdr), 0, &acc_csum); } if ((csum_calc = in_csum(PTR_CAST_CONST(void, hd), vrrppkt_len, acc_csum, &acc_csum)) && !__test_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, &vrrp->flags)) { #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ chksum_error = true; if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) && vrrp->unicast_chksum_compat == CHKSUM_COMPATIBILITY_NONE && ipv4_phdr.dst != global_data->vrrp_mcast_group4.sin_addr.s_addr) { ipv4_phdr.dst = global_data->vrrp_mcast_group4.sin_addr.s_addr; in_csum(PTR_CAST_CONST(void, &ipv4_phdr), sizeof(ipv4_phdr), 0, &acc_csum); if (!(csum_calc = in_csum(PTR_CAST_CONST(void, hd), vrrppkt_len, acc_csum, &acc_csum))) { /* Update the checksum for the pseudo header IP address */ vrrp_csum_mcast(vrrp); /* Now we can specify that we are going to use the compatibility mode */ vrrp->unicast_chksum_compat = CHKSUM_COMPATIBILITY_AUTO; log_message(LOG_INFO, "(%s) Setting unicast VRRPv3 checksum to old version", vrrp->iname); chksum_error = false; } } if (chksum_error) #endif { log_rate_limited_error(vrrp, VRRP_RLFLAG_BAD_CHECKSUM, "(%s) Invalid VRRPv3 checksum from %s", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); #ifdef _WITH_SNMP_RFC_ vrrp->stats->chk_err++; #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->proto_err_reason = checksumError; vrrp_rfcv3_snmp_proto_err_notify(vrrp); #endif #endif return VRRP_PACKET_KO; } } #ifdef _CHECKSUM_DEBUG_ if (do_checksum_debug) check_rx_checksum(vrrp, &ipv4_phdr, ip, buflen, hd, csum_calc, acc_csum); #endif } else { vrrppkt_len += VRRP_AUTH_LEN; csum_calc = in_csum(PTR_CAST_CONST(void, hd), vrrppkt_len, 0, &acc_csum); #ifdef _CHECKSUM_DEBUG_ if (do_checksum_debug) check_rx_checksum(vrrp, NULL, ip, buflen, hd, csum_calc, acc_csum); #endif if (csum_calc) { log_rate_limited_error(vrrp, VRRP_RLFLAG_BAD_CHECKSUM, "(%s) Invalid VRRPv2 checksum from %s", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); #ifdef _WITH_SNMP_RFC_ vrrp->stats->chk_err++; #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->proto_err_reason = checksumError; vrrp_rfcv3_snmp_proto_err_notify(vrrp); #endif #endif return VRRP_PACKET_KO; } } } /* check that destination address is multicast if don't have any unicast peers * and vice versa */ if (((vrrp->family == AF_INET && IN_MULTICAST(ntohl(ip->daddr))) || (vrrp->family == AF_INET6 && vrrp->multicast_pkt)) == __test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { /* So far as I can see, with IPv6 if multicasts are enabled on an interface, we will receive them * on a socket even if we haven't registered the multicast address on the socket. * If anyone knows how to stop receiving them, please raise a github issue with the details. */ log_rate_limited_error(vrrp, VRRP_RLFLAG_UNI_MULTICAST_ERR, "(%s) Expected %sicast packet but received %sicast packet from %s", vrrp->iname, __test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) ? "un" : "mult", __test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) ? "mult" : "un", inet_sockaddrtos(&vrrp->pkt_saddr)); ++vrrp->stats->addr_list_err; return VRRP_PACKET_KO; } if (vrrp->owner_ignore_adverts && vrrp->effective_priority == VRRP_PRIO_OWNER) { log_rate_limited_error(vrrp, VRRP_RLFLAG_OWNER_IGNORE_ADVER, "(%s) Dropping packet(s) from %s since we are address owner", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); return VRRP_PACKET_DROP; } /* Correct type, version, and length. Count as VRRP advertisement */ ++vrrp->stats->advert_rcvd; /* pointer to vrrp vips pkt zone */ vips = (const char *)hd + sizeof(vrrphdr_t); if (hd->naddr != vrrp->vip_cnt) { log_rate_limited_error(vrrp, VRRP_RLFLAG_WRONG_ADDR_COUNT, "(%s) expected %u VIPs but received %u", vrrp->iname, vrrp->vip_cnt, hd->naddr); ++vrrp->stats->addr_list_err; } else if (check_vip_addr) { /* * MAY verify that the IP address(es) associated with the * VRID are valid */ bool addr_ok = true; /* We have checked that the number of VIPs match, and since * all VIPs are different, if every VIP is in the advert, then * the two lists must have exactly the same entries. */ list_for_each_entry(ipaddress, &vrrp->vip, e_list) { if (!vrrp_in_chk_vips(vrrp, ipaddress, vips, hd->naddr)) { log_rate_limited_error(vrrp, VRRP_RLFLAG_VIPS_MISMATCH, "(%s) ip address associated with VRID %d" " not present in advert from %s: %s" , vrrp->iname, vrrp->vrid, inet_sockaddrtos(&vrrp->pkt_saddr) , inet_ntop(vrrp->family, vrrp->family == AF_INET6 ? &ipaddress->u.sin6_addr : (void *)&ipaddress->u.sin.sin_addr.s_addr, addr_str, sizeof(addr_str))); if (addr_ok) { ++vrrp->stats->addr_list_err; addr_ok = false; } } if (!addr_ok) break; } } /* check a unicast source address is in the unicast_peer list */ if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) && (global_data->vrrp_check_unicast_src || __test_bit(VRRP_FLAG_CHECK_UNICAST_SRC, &vrrp->flags))) { struct in_addr *saddr4 = NULL; /* Avoid compiler warnings */ struct in6_addr *saddr6 = NULL; bool found_match = false; if (vrrp->family == AF_INET6) { saddr6 = &PTR_CAST(struct sockaddr_in6, &vrrp->pkt_saddr)->sin6_addr; list_for_each_entry(up_addr, &vrrp->unicast_peer, e_list) { if (IN6_ARE_ADDR_EQUAL(saddr6, &PTR_CAST(struct sockaddr_in6, &up_addr->address)->sin6_addr)) { if (!check_ttl_hl(vrrp, up_addr)) return VRRP_PACKET_DROP; found_match = true; break; } } } else { saddr4 = &PTR_CAST(struct sockaddr_in, &vrrp->pkt_saddr)->sin_addr; list_for_each_entry(up_addr, &vrrp->unicast_peer, e_list) { if (saddr4->s_addr == PTR_CAST(struct sockaddr_in, &up_addr->address)->sin_addr.s_addr) { if (!check_ttl_hl(vrrp, up_addr)) return VRRP_PACKET_DROP; found_match = true; break; } } } if (!found_match) { log_rate_limited_error(vrrp, VRRP_RLFLAG_UNKNOWN_UNICAST_SRC, "(%s) unicast source address %s not a unicast peer", vrrp->iname, inet_ntop(vrrp->family, vrrp->family == AF_INET6 ? (void *)saddr6 : (void *)saddr4, addr_str, sizeof(addr_str))); return VRRP_PACKET_KO; } } if (hd->priority == 0) ++vrrp->stats->pri_zero_rcvd; return VRRP_PACKET_OK; } /* build IP header */ static void vrrp_build_ip4(vrrp_t *vrrp, char *buffer) { struct iphdr *ip = PTR_CAST(struct iphdr, (buffer)); ip->ihl = sizeof(struct iphdr) >> 2; ip->version = 4; /* set tos to internet network control */ ip->tos = 0xc0; ip->tot_len = (uint16_t)(sizeof (struct iphdr) + vrrp_pkt_len(vrrp)); ip->tot_len = htons(ip->tot_len); ip->id = 0; ip->frag_off = 0; ip->ttl = vrrp->ttl; /* fill protocol type --rfc2402.2 */ #ifdef _WITH_VRRP_AUTH_ ip->protocol = (vrrp->auth_type == VRRP_AUTH_AH) ? IPPROTO_AH : IPPROTO_VRRP; #else ip->protocol = IPPROTO_VRRP; #endif ip->saddr = VRRP_PKT_SADDR(vrrp); /* If using unicast peers, pick the first one */ if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { unicast_peer_t *peer = list_first_entry(&vrrp->unicast_peer, unicast_peer_t, e_list); ip->daddr = inet_sockaddrip4(&peer->address); } else ip->daddr = PTR_CAST(struct sockaddr_in, &vrrp->mcast_daddr)->sin_addr.s_addr; ip->check = 0; } #ifdef _WITH_VRRP_AUTH_ /* build IPSEC AH header */ static void vrrp_build_ipsecah(vrrp_t * vrrp, char *buffer, size_t buflen) { unsigned char digest[MD5_DIGEST_LENGTH]; struct iphdr *ip = PTR_CAST(struct iphdr, (buffer)); ipsec_ah_t *ah = PTR_CAST(ipsec_ah_t, (buffer + sizeof (struct iphdr))); /* fill in next header filed --rfc2402.2.1 */ ah->next_header = IPPROTO_VRRP; /* update IP header total length value */ ip->tot_len = htons(ntohs(ip->tot_len) + sizeof(ipsec_ah_t)); /* fill in the Payload len field */ ah->payload_len = IPSEC_AH_PLEN; /* The SPI value is filled with the ip header source address. SPI uniquely identify the Security Association (SA). This value is chosen by the recipient itself when setting up the SA. In a multicast environment, this becomes unfeasible. If left to the sender, the choice of the SPI value should be done so by the sender that it cannot possibly conflict with SPI values chosen by other entities sending IPSEC traffic to any of the receivers. To overpass this problem, the rule I have chosen to implement here is that the SPI value chosen by the sender is based on unique information such as its IP address. -- INTERNET draft : */ ah->spi = ip->saddr; /* Compute the ICV & trunc the digest to 96bits => No padding needed. -- rfc2402.3.3.3.1.1.1 & rfc2401.5 */ hmac_md5(PTR_CAST(unsigned char, buffer), buflen, NULL, 0, vrrp->auth_data, sizeof (vrrp->auth_data), digest); memcpy(ah->auth_data, digest, HMAC_MD5_TRUNC); } #endif /* build VRRPv2 header */ static void vrrp_build_vrrp_v2(vrrp_t *vrrp, char *buffer) { int i = 0; vrrphdr_t *hd = PTR_CAST(vrrphdr_t, buffer); struct in_addr *iparr; struct in6_addr *ip6arr; ip_address_t *ip_addr; /* Family independant */ hd->vers_type = (VRRP_VERSION_2 << 4) | VRRP_PKT_ADVERT; hd->vrid = vrrp->vrid; hd->priority = vrrp->effective_priority; hd->naddr = (uint8_t)((!list_empty(&vrrp->vip)) ? (uint8_t)vrrp->vip_cnt : 0); #ifdef _WITH_VRRP_AUTH_ hd->v2.auth_type = vrrp->auth_type; #else hd->v2.auth_type = VRRP_AUTH_NONE; #endif hd->v2.adver_int = (uint8_t)(vrrp->adver_int / TIMER_HZ); /* Family specific */ if (vrrp->family == AF_INET) { /* copy the ip addresses */ iparr = PTR_CAST(struct in_addr, ((char *)hd + sizeof (*hd))); list_for_each_entry(ip_addr, &vrrp->vip, e_list) iparr[i++] = ip_addr->u.sin.sin_addr; #ifdef _WITH_VRRP_AUTH_ /* copy the passwd if the authentication is VRRP_AH_PASS */ if (vrrp->auth_type == VRRP_AUTH_PASS) { unsigned vip_count = (!list_empty(&vrrp->vip)) ? vrrp->vip_cnt : 0; char *pw = (char *)hd + sizeof (*hd) + vip_count * 4; memcpy(pw, vrrp->auth_data, sizeof (vrrp->auth_data)); } #endif /* finally compute vrrp checksum */ hd->chksum = 0; hd->chksum = in_csum(PTR_CAST_CONST(void, hd), vrrp_pkt_len(vrrp), 0, NULL); } else if (vrrp->family == AF_INET6) { ip6arr = PTR_CAST(struct in6_addr, ((char *)hd + sizeof(*hd))); list_for_each_entry(ip_addr, &vrrp->vip, e_list) ip6arr[i++] = ip_addr->u.sin6_addr; /* Kernel will update checksum field. let it be 0 now. */ hd->chksum = 0; } } /* build VRRPv3 header */ static void vrrp_build_vrrp_v3(vrrp_t *vrrp, char *buffer, struct iphdr *ip) { int i = 0; vrrphdr_t *hd = PTR_CAST(vrrphdr_t, buffer); struct in_addr *iparr; struct in6_addr *ip6arr; ip_address_t *ip_addr; ipv4_phdr_t ipv4_phdr; /* Family independant */ hd->vers_type = (VRRP_VERSION_3 << 4) | VRRP_PKT_ADVERT; hd->vrid = vrrp->vrid; hd->priority = vrrp->effective_priority; hd->naddr = (uint8_t)((!list_empty(&vrrp->vip)) ? vrrp->vip_cnt : 0); hd->v3.adver_int = V3_PKT_ADVER_INT_HTON((vrrp->adver_int / TIMER_CENTI_HZ)); /* interval in centiseconds, reserved bits zero */ /* For IPv4 to calculate the checksum, the value must start as 0. * For IPv6, the kernel will update checksum field. */ hd->chksum = 0; /* Family specific */ if (vrrp->family == AF_INET) { /* copy the ip addresses */ iparr = PTR_CAST(struct in_addr, ((char *)hd + sizeof(*hd))); list_for_each_entry(ip_addr, &vrrp->vip, e_list) iparr[i++] = ip_addr->u.sin.sin_addr; if (__test_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, &vrrp->flags)) vrrp->ipv4_csum = 0; else { /* Create IPv4 pseudo-header */ ipv4_phdr.src = VRRP_PKT_SADDR(vrrp); #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ if (vrrp->unicast_chksum_compat >= CHKSUM_COMPATIBILITY_MIN_COMPAT) ipv4_phdr.dst = global_data->vrrp_mcast_group4.sin_addr.s_addr; else #endif ipv4_phdr.dst = ip->daddr; ipv4_phdr.zero = 0; ipv4_phdr.proto = IPPROTO_VRRP; ipv4_phdr.len = htons(vrrp_pkt_len(vrrp)); /* finally compute vrrp checksum */ /* coverity[callee_ptr_arith] */ in_csum(PTR_CAST_CONST(void, &ipv4_phdr), sizeof(ipv4_phdr), 0, &vrrp->ipv4_csum); } hd->chksum = in_csum(PTR_CAST_CONST(void, hd), vrrp_pkt_len(vrrp), vrrp->ipv4_csum, NULL); } else if (vrrp->family == AF_INET6) { ip6arr = PTR_CAST(struct in6_addr, ((char *)hd + sizeof(*hd))); list_for_each_entry(ip_addr, &vrrp->vip, e_list) ip6arr[i++] = ip_addr->u.sin6_addr; } } /* build VRRP header */ static void vrrp_build_vrrp(vrrp_t *vrrp, char *buffer, struct iphdr *ip_hdr) { if (vrrp->version == VRRP_VERSION_3) vrrp_build_vrrp_v3(vrrp, buffer, ip_hdr); else vrrp_build_vrrp_v2(vrrp, buffer); } /* build VRRP packet */ static void vrrp_build_pkt(vrrp_t * vrrp) { char *bufptr; if (vrrp->family == AF_INET) { /* save reference values */ bufptr = vrrp->send_buffer; /* build the ip header */ vrrp_build_ip4(vrrp, vrrp->send_buffer); /* build the vrrp header */ bufptr += sizeof(struct iphdr); #ifdef _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) bufptr += sizeof(ipsec_ah_t); #endif vrrp_build_vrrp(vrrp, bufptr, PTR_CAST(struct iphdr, vrrp->send_buffer)); #ifdef _WITH_VRRP_AUTH_ /* build the IPSEC AH header */ if (vrrp->auth_type == VRRP_AUTH_AH) vrrp_build_ipsecah(vrrp, vrrp->send_buffer, vrrp->send_buffer_size); #endif } else if (vrrp->family == AF_INET6) vrrp_build_vrrp(vrrp, vrrp->send_buffer, NULL); } /* send VRRP packet */ static int vrrp_build_ancillary_data(struct msghdr *msg, char *cbuf, sockaddr_t *src, const vrrp_t *vrrp) { struct cmsghdr *cmsg; struct in6_pktinfo *pkt; unsigned *hlim; if (src->ss_family != AF_INET6) return -1; msg->msg_control = cbuf; msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); pkt = PTR_CAST(struct in6_pktinfo, CMSG_DATA(cmsg)); memset(pkt, 0, sizeof(struct in6_pktinfo)); pkt->ipi6_addr = PTR_CAST(struct sockaddr_in6, src)->sin6_addr; if (vrrp->ifp) { #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags)) { if (vrrp->ifp == vrrp->ifp->base_ifp) { /* The base interface is in another netns */ pkt->ipi6_ifindex = vrrp->configured_ifp->ifindex; } else pkt->ipi6_ifindex = vrrp->ifp->base_ifp->ifindex; } else #endif pkt->ipi6_ifindex = vrrp->ifp->ifindex; } if (vrrp->ttl != -1 && __test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { msg->msg_controllen += CMSG_SPACE(sizeof(*hlim)); if ((cmsg = CMSG_NXTHDR(msg, cmsg))) { cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_HOPLIMIT; cmsg->cmsg_len = CMSG_LEN(sizeof(*hlim)); hlim = PTR_CAST(unsigned, CMSG_DATA(cmsg)); *hlim = vrrp->ttl; } else msg->msg_controllen -= CMSG_SPACE(sizeof(*hlim)); } return 0; } static ssize_t vrrp_send_pkt(vrrp_t * vrrp, unicast_peer_t *peer) { sockaddr_t *src = &vrrp->saddr; struct msghdr msg; struct iovec iov; char cbuf[256] __attribute__((aligned(__alignof__(struct cmsghdr)))); /* Build the message data */ memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = vrrp->send_buffer; iov.iov_len = vrrp->send_buffer_size; /* glibc's CMSG_NXTHDR requires the buffer to have been initialised to all 0s */ if (vrrp->family == AF_INET6) memset(cbuf, 0, sizeof(cbuf)); /* Unicast sending path */ if (peer && peer->address.ss_family == AF_INET) { msg.msg_name = &peer->address; msg.msg_namelen = sizeof(struct sockaddr_in); } else if (peer && peer->address.ss_family == AF_INET6) { msg.msg_name = &peer->address; msg.msg_namelen = sizeof(struct sockaddr_in6); vrrp_build_ancillary_data(&msg, cbuf, src, vrrp); } else if (vrrp->family == AF_INET) { /* Multicast sending path */ msg.msg_name = &vrrp->mcast_daddr; msg.msg_namelen = sizeof(struct sockaddr_in); } else if (vrrp->family == AF_INET6) { msg.msg_name = &vrrp->mcast_daddr; msg.msg_namelen = sizeof(struct sockaddr_in6); vrrp_build_ancillary_data(&msg, cbuf, src, vrrp); } #ifdef _CHECKSUM_DEBUG_ if (vrrp->family == AF_INET && do_checksum_debug) check_tx_checksum(vrrp, peer); #endif /* Send the packet */ return sendmsg(vrrp->sockets->fd_out, &msg, (peer) ? 0 : MSG_DONTROUTE); } /* Allocate the sending buffer */ static void vrrp_alloc_send_buffer(vrrp_t * vrrp) { vrrp->send_buffer_size = vrrp_adv_len(vrrp); vrrp->send_buffer = MALLOC(vrrp->send_buffer_size); } /* send VRRP advertisement */ void vrrp_send_adv(vrrp_t * vrrp, uint8_t prio) { unicast_peer_t *peer; if (!vrrp->sockets || vrrp->sockets->fd_out == -1) return; #ifdef _HAVE_VRRP_VMAC_ if (vrrp->saddr.ss_family == AF_UNSPEC && vrrp->family == AF_INET6 && (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) #ifdef _HAVE_VRRP_IPVLAN_ || __test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) #endif )) { if (IN6_IS_ADDR_UNSPECIFIED(&vrrp->ifp->sin6_addr)) { log_message(LOG_INFO, "No address yet for %s", vrrp->ifp->ifname); return; } inet_ip6tosockaddr(&vrrp->ifp->sin6_addr, &vrrp->saddr); } #endif /* build the packet */ vrrp_update_pkt(vrrp, prio, NULL); /* Send the packet, but don't log an error if it is a prio 0 message * and the interface is down. */ vrrp->last_advert_sent = time_now; if (!__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { // What if mcast_src_ip is configured? if (vrrp_send_pkt(vrrp, NULL) == -1 && (prio != VRRP_PRIO_STOP || errno != ENETUNREACH || (vrrp->ifp && IF_FLAGS_UP(vrrp->ifp)))) log_message(LOG_INFO, "(%s): send advert error %d (%m)", vrrp->iname, errno); } else { list_for_each_entry(peer, &vrrp->unicast_peer, e_list) { if (vrrp->family == AF_INET) vrrp_update_pkt(vrrp, prio, &peer->address); if (vrrp_send_pkt(vrrp, peer) == -1 && (prio != VRRP_PRIO_STOP || errno != ENETUNREACH || (vrrp->ifp && IF_FLAGS_UP(vrrp->ifp)))) log_message(LOG_INFO, "(%s) Cant send advert to %s (%m)" , vrrp->iname, inet_sockaddrtos(&peer->address)); } } ++vrrp->stats->advert_sent; } /* Gratuitous ARP on each VIP */ static void vrrp_send_update(vrrp_t * vrrp, ip_address_t * ipaddress, bool log_msg, unsigned rep) { const char *msg; char addr_str[INET6_ADDRSTRLEN]; if (log_msg && __test_bit(LOG_DETAIL_BIT, &debug)) { if (!IP_IS6(ipaddress)) { msg = "gratuitous ARPs"; inet_ntop(AF_INET, &ipaddress->u.sin.sin_addr, addr_str, sizeof(addr_str)); } else { msg = "Unsolicited Neighbour Adverts"; inet_ntop(AF_INET6, &ipaddress->u.sin6_addr, addr_str, sizeof(addr_str)); } log_message(LOG_INFO, "(%s) Sending/queueing %s on %s for %s", vrrp->iname, msg, IF_NAME(ipaddress->ifp), addr_str); } if (!IP_IS6(ipaddress)) send_gratuitous_arp(ipaddress, rep); else ndisc_send_unsolicited_na(ipaddress, rep); } void vrrp_send_link_update(vrrp_t * vrrp, unsigned rep) { unsigned j; ip_address_t *ip_addr; /* Only send gratuitous ARP if VIP are set */ if (!VRRP_VIP_ISSET(vrrp)) return; /* send gratuitous arp for each virtual ip. * Looping rep times through all VIPs of the vrrp instance doesn't * seem very efficient, but I haven't thought of a better way when * the GARP/NA may either be sent or queued. */ for (j = 0; j < rep; j++) { list_for_each_entry(ip_addr, &vrrp->vip, e_list) vrrp_send_update(vrrp, ip_addr, !j, rep); list_for_each_entry(ip_addr, &vrrp->evip, e_list) vrrp_send_update(vrrp, ip_addr, !j, rep); } } #ifdef _HAVE_VRRP_VMAC_ void vrrp_send_vmac_update(vrrp_t *vrrp) { struct ifs { ifindex_t ifindex; list_head_t e_list; }; ip_address_t *ip_addr; list_head_t *vip_list; LIST_HEAD_INITIALIZE(if_list); struct ifs *if_entry, *next_if_entry; bool already_done; /* Only send gratuitous ARP if VIP are set */ if (!VRRP_VIP_ISSET(vrrp)) return; /* send a gratuitous arp for each VMAC interface that is not sending adverts */ for (vip_list = &vrrp->vip; vip_list; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL) { list_for_each_entry(ip_addr, vip_list, e_list) { /* Don't send for non VMAC i/fs unless specified */ if (!ip_addr->ifp->is_ours && !__test_bit(VRRP_FLAG_VMAC_GARP_ALL_IF, &vrrp->flags)) continue; /* Don't send for our own interface unless xmit_base */ if (ip_addr->ifp == vrrp->ifp && !__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags)) continue; /* Check we haven't already sent on this interface */ already_done = false; list_for_each_entry_reverse(if_entry, &if_list, e_list) { if (ip_addr->ifp->ifindex == if_entry->ifindex) { already_done = true; break; } } if (already_done) continue; vrrp_send_update(vrrp, ip_addr, true, 1); /* Save interface ifindex to avoid sending on that interface again */ PMALLOC(if_entry); INIT_LIST_HEAD(&if_entry->e_list); if_entry->ifindex = ip_addr->ifp->ifindex; list_add_tail(&if_entry->e_list, &if_list); } } /* Free the list of interface indices we have sent on */ list_for_each_entry_safe(if_entry, next_if_entry, &if_list, e_list) FREE(if_entry); } #endif static void vrrp_remove_delayed_arp(vrrp_t *vrrp) { ip_address_t *ip_addr; list_for_each_entry(ip_addr, &vrrp->vip, e_list) { ip_addr->garp_gna_pending = 0; list_del_init(&ip_addr->garp_gna_list); } list_for_each_entry(ip_addr, &vrrp->evip, e_list) { ip_addr->garp_gna_pending = 0; list_del_init(&ip_addr->garp_gna_list); } } /* becoming master */ static void vrrp_state_become_master(vrrp_t * vrrp) { ++vrrp->stats->become_master; /* If both us and another system claim to be the address owner then * we may have reduced our priority to 254 to ensure there are not * 2 (or more) masters. The other system must have gone away now, * so restore our priority. */ if (vrrp->base_priority == VRRP_PRIO_OWNER && vrrp->effective_priority != VRRP_PRIO_OWNER) { log_message(LOG_INFO, "(%s) Restoring our priority to %d since other address owner has disappeared", vrrp->iname, VRRP_PRIO_OWNER); vrrp->effective_priority = VRRP_PRIO_OWNER; vrrp->total_priority = VRRP_PRIO_OWNER; } if (vrrp->version == VRRP_VERSION_3 && __test_bit(LOG_DETAIL_BIT, &debug) && vrrp->master_adver_int != vrrp->adver_int) { log_message(LOG_INFO, "(%s) changing advert interval from %ums to locally configured %ums", vrrp->iname, vrrp->master_adver_int / (TIMER_HZ / 1000), vrrp->adver_int / (TIMER_HZ / 1000)); vrrp->master_adver_int = vrrp->adver_int; } /* add the ip addresses */ #ifdef _WITH_FIREWALL_ vrrp_handle_accept_mode(vrrp, IPADDRESS_ADD, false); #endif if (!list_empty(&vrrp->vip)) vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_VIP_TYPE, false); if (!list_empty(&vrrp->evip)) vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_EVIP_TYPE, false); vrrp->vipset = true; /* add virtual routes */ if (!list_empty(&vrrp->vroutes)) vrrp_handle_iproutes(vrrp, IPROUTE_ADD, false); /* add virtual rules */ if (!list_empty(&vrrp->vrules)) vrrp_handle_iprules(vrrp, IPRULE_ADD, false); kernel_netlink_poll(); vrrp_send_link_update(vrrp, vrrp->garp_rep); if (vrrp->garp_delay) thread_add_timer(master, vrrp_gratuitous_arp_thread, vrrp, vrrp->garp_delay); if (timerisset(&vrrp->garp_refresh)) thread_add_timer(master, vrrp_gratuitous_arp_refresh_thread, vrrp, vrrp->garp_delay + timer_long(vrrp->garp_refresh)); #ifdef _HAVE_VRRP_VMAC_ if (timerisset(&vrrp->vmac_garp_intvl)) thread_add_timer(master, vrrp_gratuitous_arp_vmac_update_thread, vrrp, vrrp->garp_delay + timer_long(vrrp->vmac_garp_intvl)); #endif /* Check if notify is needed */ send_instance_notifies(vrrp); #ifdef _WITH_LVS_ /* Check if sync daemon handling is needed */ if (global_data->lvs_syncd.vrrp == vrrp) ipvs_syncd_master(&global_data->lvs_syncd); #endif vrrp->last_transition = timer_now(); } void vrrp_state_goto_master(vrrp_t * vrrp) { if (vrrp->sync && !vrrp_sync_can_goto_master(vrrp)) { vrrp->wantstate = VRRP_STATE_MAST; return; } /* Clear the rate-limited log error flags */ vrrp->rlflags = 0; #if defined _WITH_VRRP_AUTH_ /* If becoming MASTER in IPSEC AH AUTH, we reset the anti-replay */ if (vrrp->ipsecah_counter.cycle) { vrrp->ipsecah_counter.cycle = false; vrrp->ipsecah_counter.seq_number = 0; } #endif #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->master_reason = vrrp->stats->next_master_reason; #endif vrrp->state = VRRP_STATE_MAST; vrrp_init_instance_sands(vrrp); vrrp_state_master_tx(vrrp); } /* leaving master state */ void vrrp_restore_interface(vrrp_t * vrrp, bool advF, bool force) { /* if we stop vrrp, warn the other routers to speed up the recovery */ if (advF) { vrrp_send_adv(vrrp, VRRP_PRIO_STOP); ++vrrp->stats->pri_zero_sent; if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) sent 0 priority", vrrp->iname); } /* remove virtual rules */ if (!list_empty(&vrrp->vrules)) vrrp_handle_iprules(vrrp, IPRULE_DEL, force); /* remove virtual routes */ if (!list_empty(&vrrp->vroutes)) vrrp_handle_iproutes(vrrp, IPROUTE_DEL, force); /* empty the delayed arp list */ vrrp_remove_delayed_arp(vrrp); /* * Remove the ip addresses. * * If started with "--dont-release-vrrp" then try to remove * addresses even if we didn't add them during this run. * * If "--release-vips" is set then try to release any virtual addresses. * kill -1 tells keepalived to reread its config. If a config change * (such as lower priority) causes a state transition to backup then * keepalived doesn't remove the VIPs. Then we have duplicate IP addresses * on both master/backup. */ if (force || VRRP_VIP_ISSET(vrrp) || __test_bit(DONT_RELEASE_VRRP_BIT, &debug) || __test_bit(RELEASE_VIPS_BIT, &debug)) { if (!list_empty(&vrrp->vip)) vrrp_handle_ipaddress(vrrp, IPADDRESS_DEL, VRRP_VIP_TYPE, force); if (!list_empty(&vrrp->evip)) vrrp_handle_ipaddress(vrrp, IPADDRESS_DEL, VRRP_EVIP_TYPE, force); #ifdef _WITH_FIREWALL_ vrrp_handle_accept_mode(vrrp, IPADDRESS_DEL, force); #endif vrrp->vipset = false; } } void vrrp_state_leave_master(vrrp_t * vrrp, bool advF) { #ifdef _WITH_LVS_ if (VRRP_VIP_ISSET(vrrp)) { /* Check if sync daemon handling is needed */ if (global_data->lvs_syncd.vrrp == vrrp) ipvs_syncd_backup(&global_data->lvs_syncd); } #endif /* Clear the rate-limited log error flags */ vrrp->rlflags = 0; /* set the new vrrp state */ if (vrrp->wantstate == VRRP_STATE_BACK) { log_message(LOG_INFO, "(%s) Entering BACKUP STATE", vrrp->iname); vrrp->preempt_time.tv_sec = 0; } else if (vrrp->wantstate == VRRP_STATE_FAULT) { log_message(LOG_INFO, "(%s) Entering FAULT STATE", vrrp->iname); /* If there is no address on the interface we cannot sent an IPv6 advert */ if (vrrp->family == AF_INET || vrrp->saddr.ss_family != AF_UNSPEC) vrrp_send_adv(vrrp, VRRP_PRIO_STOP); } else { log_message(LOG_INFO, "(%s) vrrp_state_leave_master called with invalid wantstate %d", vrrp->iname, vrrp->wantstate); return; } vrrp_restore_interface(vrrp, advF, false); vrrp->state = vrrp->wantstate; send_instance_notifies(vrrp); /* Set the down timer */ vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); vrrp_init_instance_sands(vrrp); ++vrrp->stats->release_master; vrrp->last_transition = timer_now(); if (vrrp->rogue_timer_thread) { thread_cancel(vrrp->rogue_timer_thread); vrrp->rogue_timer_thread = NULL; } else vrrp->rogue_counter = 0; } void vrrp_state_leave_fault(vrrp_t * vrrp) { /* set the new vrrp state */ if (vrrp->wantstate == VRRP_STATE_MAST) vrrp_state_goto_master(vrrp); else { if (vrrp->state != vrrp->wantstate) log_message(LOG_INFO, "(%s) Entering %s STATE", vrrp->iname, vrrp->wantstate == VRRP_STATE_BACK ? "BACKUP" : "FAULT"); if (vrrp->wantstate == VRRP_STATE_FAULT && vrrp->state == VRRP_STATE_MAST) { vrrp_send_adv(vrrp, VRRP_PRIO_STOP); vrrp_restore_interface(vrrp, false, false); } vrrp->state = vrrp->wantstate; send_instance_notifies(vrrp); if (vrrp->state == VRRP_STATE_BACK) vrrp->preempt_time.tv_sec = 0; } /* Set the down timer */ vrrp->master_adver_int = vrrp->adver_int; vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); vrrp_init_instance_sands(vrrp); vrrp->last_transition = timer_now(); } static bool check_debounce_timers(vrrp_t *vrrp, unsigned advert_int) { bool changed = false; unsigned max_timer; if (vrrp->down_timer_adverts == 1 || !vrrp->ifp) { /* There can be no debounce timer */ return false; } max_timer = (vrrp->down_timer_adverts - 1) * advert_int - advert_int / 256; if (IF_BASE_IFP(vrrp->ifp)->down_debounce_timer > max_timer) { changed = true; if (IF_BASE_IFP(vrrp->ifp)->up_debounce_timer == IF_BASE_IFP(vrrp->ifp)->down_debounce_timer) IF_BASE_IFP(vrrp->ifp)->up_debounce_timer = max_timer; IF_BASE_IFP(vrrp->ifp)->down_debounce_timer = max_timer; } #ifdef _HAVE_VRRP_VMAC_ if (vrrp->ifp != vrrp->ifp->base_ifp) { if (vrrp->ifp->down_debounce_timer > max_timer) { changed = true; if (vrrp->ifp->up_debounce_timer == vrrp->ifp->down_debounce_timer) vrrp->ifp->up_debounce_timer = max_timer; vrrp->ifp->down_debounce_timer = max_timer; } } #endif return changed; } static void update_master_adver_int(vrrp_t *vrrp, unsigned master_adver_int) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) advertisement interval updated from %ums to %ums by master", vrrp->iname, vrrp->master_adver_int / (TIMER_HZ / 1000), master_adver_int / (TIMER_HZ / 1000)); if (master_adver_int < vrrp->master_adver_int) { /* Check that the interface up/down timers do not exceed twice the * advert interval. */ if (check_debounce_timers(vrrp, master_adver_int) && __test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "%s: lower advert_int reducing interface %s debounce timer(s)", vrrp->iname, IF_BASE_IFP(vrrp->ifp)->ifname); } vrrp->master_adver_int = master_adver_int; } /* BACKUP state processing */ void vrrp_state_backup(vrrp_t *vrrp, const vrrphdr_t *hd, const char *buf, ssize_t buflen) { ssize_t ret = 0; unsigned master_adver_int; timeval_t new_ms_down_timer; bool ignore_advert = false; bool master_change = false; /* Process the incoming packet */ /* Check if the saddr has changed */ if (vrrp->master_saddr.ss_family != vrrp->pkt_saddr.ss_family || (vrrp->pkt_saddr.ss_family == AF_INET && PTR_CAST(struct sockaddr_in, &vrrp->pkt_saddr)->sin_addr.s_addr != PTR_CAST(struct sockaddr_in, &vrrp->master_saddr)->sin_addr.s_addr) || (vrrp->pkt_saddr.ss_family == AF_INET6 && !IN6_ARE_ADDR_EQUAL(&PTR_CAST(struct sockaddr_in6, &vrrp->pkt_saddr)->sin6_addr, &PTR_CAST(struct sockaddr_in6, &vrrp->master_saddr)->sin6_addr))) { master_change = true; /* We want to reset the rate-limit flags since the master has changed */ vrrp->rlflags = 0 ; if (__test_bit(LOG_DETAIL_BIT, &debug)) { if (vrrp->master_saddr.ss_family == AF_UNSPEC) log_message(LOG_INFO, "(%s) master set to %s", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); else { char old_master[INET6_ADDRSTRLEN]; strcpy(old_master, inet_sockaddrtos(&vrrp->master_saddr)); log_message(LOG_INFO, "(%s) master changed from %s to %s", vrrp->iname, old_master, inet_sockaddrtos(&vrrp->pkt_saddr)); } } } ret = vrrp_check_packet(vrrp, hd, buf, buflen, master_change || !__test_bit(VRRP_FLAG_SKIP_CHECK_ADV_ADDR, &vrrp->flags)); if (ret != VRRP_PACKET_OK) ignore_advert = true; else { /* If we and another instance were both configured as priority 255 * we may have reduced our priority to avoid a conflict. * We are now no longer receiving priority 255 adverts from the same * remote system, so set our priority back to 255. */ if (vrrp->base_priority == VRRP_PRIO_OWNER && vrrp->effective_priority == VRRP_PRIO_OWNER - 1 && (master_change || hd->priority != VRRP_PRIO_OWNER)) { log_message(LOG_INFO, "(%s) Restoring our priority to %d since received advert with lower priority", vrrp->iname, VRRP_PRIO_OWNER); vrrp->effective_priority = VRRP_PRIO_OWNER; vrrp->total_priority = VRRP_PRIO_OWNER; } if (hd->priority == 0) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Backup received priority 0 advertisement", vrrp->iname); vrrp->ms_down_timer = VRRP_TIMER_SKEW(vrrp); #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->next_master_reason = VRRPV3_MASTER_REASON_PRIORITY; #endif } else if (__test_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags) || hd->priority >= vrrp->effective_priority || (vrrp->preempt_delay && (!vrrp->preempt_time.tv_sec || timercmp(&vrrp->preempt_time, &time_now, >)))) { /* We are accepting the advert */ if (vrrp->version == VRRP_VERSION_3) { master_adver_int = V3_PKT_ADVER_INT_NTOH(hd->v3.adver_int) * TIMER_CENTI_HZ; /* As per RFC5798, set Master_Adver_Interval to Adver Interval contained * in the ADVERTISEMENT */ if (vrrp->master_adver_int != master_adver_int) update_master_adver_int(vrrp, master_adver_int); } vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); vrrp->master_saddr = vrrp->pkt_saddr; vrrp->master_priority = hd->priority; #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->next_master_reason = VRRPV3_MASTER_REASON_MASTER_NO_RESPONSE; #endif if (vrrp->preempt_delay) { if (hd->priority >= vrrp->effective_priority) { if (vrrp->preempt_time.tv_sec) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) stop preempt delay", vrrp->iname); vrrp->preempt_time.tv_sec = 0; } } else if (!vrrp->preempt_time.tv_sec) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) start preempt delay (%lu.%6.6lu)", vrrp->iname, vrrp->preempt_delay / TIMER_HZ, vrrp->preempt_delay % TIMER_HZ); vrrp->preempt_time = timer_add_long(timer_now(), vrrp->preempt_delay); } } /* We might have been held in backup by a sync group, but if * ms_down_timer had expired, we would have wanted MASTER state. * Now we have received a higher priority advert, we want to be in BACKUP state. */ vrrp->wantstate = VRRP_STATE_BACK; } else { /* !nopreempt and lower priority advert and any preempt delay timer has expired */ log_message(LOG_INFO, "(%s) received lower priority (%d) advert from %s - discarding", vrrp->iname, hd->priority, inet_sockaddrtos(&vrrp->pkt_saddr)); ignore_advert = true; #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->next_master_reason = VRRPV3_MASTER_REASON_PREEMPTED; #endif /* We still want to record the master's address for SNMP purposes */ vrrp->master_saddr = vrrp->pkt_saddr; } } if (ignore_advert) { /* We need to reduce the down timer since we have ignored the advert */ set_time_now(); timersub(&vrrp->sands, &time_now, &new_ms_down_timer); vrrp->ms_down_timer = new_ms_down_timer.tv_sec < 0 ? 0 : (uint32_t)(new_ms_down_timer.tv_sec * TIMER_HZ + new_ms_down_timer.tv_usec); } } static void vrrp_rogue_timer_thread(thread_ref_t thread) { vrrp_t *vrrp = THREAD_ARG(thread); /* We have not received a further advert, so we continue as master */ log_message(LOG_INFO, "(%s): rogue address owner appears to have stopped advertising", vrrp->iname); vrrp->rogue_timer_thread = NULL; } /* MASTER state processing */ void vrrp_state_master_tx(vrrp_t * vrrp) { /* If we are transitioning to master the old master needs to * remove the VIPs before we send the gratuitous ARPs, so send * the advert first. */ vrrp_send_adv(vrrp, vrrp->effective_priority); if (!VRRP_VIP_ISSET(vrrp)) { log_message(LOG_INFO, "(%s) Entering MASTER STATE" , vrrp->iname); vrrp_state_become_master(vrrp); } else if (vrrp->base_priority == VRRP_PRIO_OWNER) { if (vrrp->rogue_counter && !--vrrp->rogue_counter) { /* For an explanation of what is happening here, see * vrrp_state_master_rx(). */ unsigned long timer = max(vrrp->rogue_adver_int, vrrp->adver_int); timer = (timer * 12) / 10; // Apply the "a bit more than" - 1.2 here vrrp->rogue_timer_thread = thread_add_timer(master, vrrp_rogue_timer_thread, vrrp, timer); } } } static int vrrp_saddr_cmp(sockaddr_t *addr, vrrp_t *vrrp) { interface_t *ifp = vrrp->ifp; /* Simple sanity */ if (vrrp->saddr.ss_family && addr->ss_family != vrrp->saddr.ss_family) return 0; /* Configured source IP address */ if (vrrp->saddr.ss_family) return inet_sockaddrcmp(addr, &vrrp->saddr); if (!ifp) return 0; /* Default interface source IP address */ if (addr->ss_family == AF_INET) return inet_inaddrcmp(addr->ss_family, &PTR_CAST(struct sockaddr_in, addr)->sin_addr, &ifp->sin_addr); if (addr->ss_family == AF_INET6) return inet_inaddrcmp(addr->ss_family, &PTR_CAST(struct sockaddr_in6, addr)->sin6_addr, &ifp->sin6_addr); return 0; } // TODO Return true to leave master state, false to remain master // TODO check all uses of master_adver_int (and simplify for VRRPv2) // TODO check all uses of effective_priority // TODO wantstate must be >= state // TODO SKEW_TIME should use master_adver_int USUALLY!!! // TODO check all use of ipsecah_counter, including cycle, and when we set seq_number bool vrrp_state_master_rx(vrrp_t * vrrp, const vrrphdr_t *hd, const char *buf, ssize_t buflen) { ssize_t ret; #ifdef _WITH_VRRP_AUTH_ const ipsec_ah_t *ah; #endif unsigned master_adver_int; int addr_cmp; vrrp_t *isync; // TODO - could we get here with wantstate == FAULT and STATE != FAULT? /* return on link failure */ // TODO - not needed??? if (vrrp->wantstate == VRRP_STATE_FAULT) { vrrp->master_adver_int = vrrp->adver_int; vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); vrrp->state = VRRP_STATE_FAULT; send_instance_notifies(vrrp); vrrp->last_transition = timer_now(); return true; } /* Process the incoming packet */ ret = vrrp_check_packet(vrrp, hd, buf, buflen, true); if (ret != VRRP_PACKET_OK) return false; addr_cmp = vrrp_saddr_cmp(&vrrp->pkt_saddr, vrrp); if (hd->priority == 0 || (vrrp->higher_prio_send_advert && (hd->priority > vrrp->effective_priority || (hd->priority == vrrp->effective_priority && addr_cmp > 0)))) { vrrp_send_adv(vrrp, vrrp->effective_priority); if (hd->priority == 0) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Master received priority 0 message", vrrp->iname); return false; } } if (hd->priority == vrrp->effective_priority) { if (addr_cmp == 0) log_message(LOG_INFO, "(%s) WARNING - equal priority advert received from remote host with our IP address.", vrrp->iname); else if (vrrp->effective_priority == VRRP_PRIO_OWNER) { /* If we are configured as the address owner (priority == 255), and we receive an advertisement * from another system indicating it is also the address owner, then there is a clear conflict. */ if (addr_cmp > 0) { /* Report a configuration error, but since our primary IP address is lower, we will revert to backup. */ log_message(LOG_INFO, "(%s) CONFIGURATION ERROR: local instance and %s are both configured as address owner, please resolve", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); } else if (vrrp->rogue_timer_thread) { /* We are still receiving adverts when the rogue should have stopped * if it implements keepalived's rogue handling. * We must fall back now to stop there being two masters. */ log_message(LOG_INFO, "(%s) %s is still advertising as address owner, please resolve - reducing our priority", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); thread_cancel(vrrp->rogue_timer_thread); vrrp->rogue_timer_thread = NULL; vrrp->effective_priority = VRRP_PRIO_OWNER - 1; vrrp->total_priority = VRRP_PRIO_OWNER - 1; } else { /* We have the higher primary IP address. * We need to see if the remote, rogue, address owner will stop * sending adverts. We wait for us to send two adverts, and then * "a bit more than" max(our adver_int, rogue's adver_int). If we * still receive an advert after we have sent two more adverts, * and before the timer expires, then we assume that the rogue * will not back off and we back off ourself. */ if (vrrp->version == VRRP_VERSION_2) vrrp->rogue_adver_int = hd->v2.adver_int * TIMER_HZ; else vrrp->rogue_adver_int = V3_PKT_ADVER_INT_NTOH(hd->v3.adver_int) * TIMER_CENTI_HZ; if (!vrrp->rogue_counter) { log_message(LOG_INFO, "(%s) CONFIGURATION ERROR: local instance and %s are both configured as address owner, please resolve", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr)); vrrp->rogue_counter = 2; } } } } if (hd->priority < vrrp->effective_priority || (hd->priority == vrrp->effective_priority && addr_cmp < 0)) { /* We receive a lower prio adv we just refresh remote ARP cache */ log_message(LOG_INFO, "(%s) Received advert from %s with lower priority %d, ours %d%s", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), hd->priority, vrrp->effective_priority, !vrrp->lower_prio_no_advert ? ", forcing new election" : ""); #ifdef _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) { ah = PTR_CAST_CONST(ipsec_ah_t, buf + sizeof(struct iphdr)); log_message(LOG_INFO, "(%s) IPSEC-AH : Syncing seq_num" " - Increment seq" , vrrp->iname); // TODO - why is seq_number taken from lower priority advert? vrrp->ipsecah_counter.seq_number = ntohl(ah->seq_number) + 1; vrrp->ipsecah_counter.cycle = false; } #endif if (!vrrp->lower_prio_no_advert) vrrp_send_adv(vrrp, vrrp->effective_priority); if (vrrp->garp_lower_prio_rep) { vrrp_send_link_update(vrrp, vrrp->garp_lower_prio_rep); if (vrrp->garp_lower_prio_delay) thread_add_timer(master, vrrp_lower_prio_gratuitous_arp_thread, vrrp, vrrp->garp_lower_prio_delay); } /* If we are a member of a sync group, send GARP messages for any other member * of the group that has garp_lower_prio_rep set. * The reason for this is we must have been in some sort of split brain situation, * or this keepalived process was not scheduled to run for a while, and a lower * priority instance has become master, causing it to send adverts and GARP * messages. When we send this advert, all the sync group members on the lower * priority instance will transition to backup state, and we will not see * adverts from those members of the sync group. However, the other VRRP * instances need to refresh ARP caches. */ if (vrrp->sync) { list_for_each_entry(isync, &vrrp->sync->vrrp_instances, s_list) { if (isync == vrrp) continue; if (!isync->garp_lower_prio_rep) continue; vrrp_send_link_update(isync, isync->garp_lower_prio_rep); if (isync->garp_lower_prio_delay) thread_add_timer(master, vrrp_lower_prio_gratuitous_arp_thread, isync, isync->garp_lower_prio_delay); } } /* If a lower priority router has transitioned to master, there has presumably * been an intermittent communications break between the master and backup. It * appears that servers in an Amazon AWS environment can experience this. * The problem then occurs if a notify_master script is executed on the backup * that has just transitioned to master and the script executes something like * a `aws ec2 assign-private-ip-addresses` command, thereby removing the address * from the 'proper' master. Executing notify_master_rx_lower_pri notification * allows the 'proper' master to recover the secondary addresses. */ send_event_notify(vrrp, VRRP_EVENT_MASTER_RX_LOWER_PRI); return false; } if (hd->priority > vrrp->effective_priority || (hd->priority == vrrp->effective_priority && addr_cmp > 0)) { if (hd->priority > vrrp->effective_priority && vrrp->base_priority != VRRP_PRIO_OWNER) log_message(LOG_INFO, "(%s) Master received advert from %s with higher priority %d, ours %d", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), hd->priority, vrrp->effective_priority); else log_message(LOG_INFO, "(%s) Master received advert from %s with same priority %d but higher IP address than ours", vrrp->iname, inet_sockaddrtos(&vrrp->pkt_saddr), hd->priority); #ifdef _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) vrrp->ipsecah_counter.cycle = false; #endif if (vrrp->version == VRRP_VERSION_3) { master_adver_int = V3_PKT_ADVER_INT_NTOH(hd->v3.adver_int) * TIMER_CENTI_HZ; /* As per RFC5798, set Master_Adver_Interval to Adver Interval contained * in the ADVERTISEMENT */ if (vrrp->master_adver_int != master_adver_int) update_master_adver_int(vrrp, master_adver_int); } vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); vrrp->master_saddr = vrrp->pkt_saddr; vrrp->master_priority = hd->priority; vrrp->wantstate = VRRP_STATE_BACK; vrrp->state = VRRP_STATE_BACK; return true; } /* We have received an equal priority advert from our own primary IP address */ return false; } static void vrrp_thread_timeout_handler(unsigned timeout) { vrrp_t *vrrp; timeval_t advert_expires_time; bool logged_timeout_action = false; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->state != VRRP_STATE_MAST || !vrrp->highest_other_priority) continue; advert_expires_time.tv_sec = 0; advert_expires_time.tv_usec = vrrp->adver_int * 3 + (vrrp->version == VRRP_VERSION_2 ? (256U - vrrp->highest_other_priority) * 1000000 / 256 : (256U - vrrp->highest_other_priority) * vrrp->adver_int / 256); if (advert_expires_time.tv_usec >= 1000000) { advert_expires_time.tv_sec += advert_expires_time.tv_usec / 1000000; advert_expires_time.tv_usec = advert_expires_time.tv_usec % 1000000; } timeradd(&vrrp->last_advert_sent, &advert_expires_time, &advert_expires_time); if (timercmp(&advert_expires_time, &time_now, <=)) { if (!logged_timeout_action) { log_message(LOG_INFO, "VRRP thread timer expired %u.%6.6u seconds ago", timeout / 1000000, timeout % 1000000); logged_timeout_action = true; } vrrp->wantstate = VRRP_STATE_BACK; vrrp_state_leave_master(vrrp, false); } } } void add_vrrp_to_interface(vrrp_t *vrrp, interface_t *ifp, int weight, bool reverse, bool log_addr, track_t type) { char addr_str[INET6_ADDRSTRLEN]; tracking_obj_t *top = NULL; track_t old_type; if (list_empty(&ifp->tracking_vrrp)) { if (log_addr && __test_bit(LOG_DETAIL_BIT, &debug)) { if (ifp->sin_addr.s_addr) { inet_ntop(AF_INET, &ifp->sin_addr, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Assigned address %s for interface %s" , addr_str, ifp->ifname); } if (!IN6_IS_ADDR_UNSPECIFIED(&ifp->sin6_addr)) { inet_ntop(AF_INET6, &ifp->sin6_addr, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Assigned address %s for interface %s" , addr_str, ifp->ifname); } } } else { /* Check if this is already in the list, and adjust the weight appropriately */ list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { if (top->obj.vrrp == vrrp) { old_type = top->type; if (top->type & (TRACK_VRRP | TRACK_IF | TRACK_SG) && type & (TRACK_VRRP | TRACK_IF | TRACK_SG) && top->weight != VRRP_NOT_TRACK_IF && weight != VRRP_NOT_TRACK_IF) report_config_error(CONFIG_GENERAL_ERROR, "(%s) track_interface %s is configured on VRRP instance and sync group. Remove vrrp instance or sync group config", vrrp->iname, ifp->ifname); /* Update the weight appropriately. We will use the sync group's * weight unless the vrrp setting is unweighted. */ if (type != TRACK_VRRP_DYNAMIC && top->weight && weight != VRRP_NOT_TRACK_IF) { top->weight = weight; top->weight_multiplier = reverse ? -1 : 1; } top->type |= type; /* If we have set the dynamic bit, move top to head of list */ if (!(old_type & TRACK_VRRP_DYNAMIC) && type == TRACK_VRRP_DYNAMIC) { list_del_init(&top->e_list); list_head_add(&top->e_list, &ifp->tracking_vrrp); } return; } } } /* Not in list so add */ PMALLOC(top); INIT_LIST_HEAD(&top->e_list); top->obj.vrrp = vrrp; top->weight = weight; top->weight_multiplier = reverse ? -1 : 1; top->type = type; /* We want the dynamic entries at the start of the list, so that it * will be processed before a weighted track */ if (type == TRACK_VRRP_DYNAMIC) list_head_add(&top->e_list, &ifp->tracking_vrrp); else list_add_tail(&top->e_list, &ifp->tracking_vrrp); } void del_vrrp_from_interface(vrrp_t *vrrp, interface_t *ifp) { tracking_obj_t *top, *top_tmp; list_for_each_entry_safe(top, top_tmp, &ifp->tracking_vrrp, e_list) { if (top->obj.vrrp == vrrp && (top->type & TRACK_VRRP_DYNAMIC)) { if (!IF_ISUP(ifp) && !__test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags)) { #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && VRRP_CONFIGURED_IFP(vrrp) == ifp) __clear_bit(VRRP_FAULT_FL_BASE_INTERFACE_DOWN, &vrrp->flags_if_fault); else #endif { /* assuming there is only one tracked interface per vrrp : to be checked */ __clear_bit(VRRP_FAULT_FL_INTERFACE_DOWN, &vrrp->flags_if_fault); } } top->type &= ~TRACK_VRRP_DYNAMIC; if (!top->type) free_tracking_obj(top); else { list_del_init(&top->e_list); list_add_tail(&top->e_list, &ifp->tracking_vrrp); } return; } /* The dynamic entries are at the start of the list */ if (!(top->type & TRACK_VRRP_DYNAMIC)) return; } } /* check for minimum configuration requirements */ static bool chk_min_cfg(vrrp_t *vrrp) { if (vrrp->vrid == 0) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) the virtual router id must be set", vrrp->iname); return false; } if (!vrrp->ifp) { if (!__test_bit(VRRP_FLAG_UNICAST_CONFIGURED, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Unknown interface!", vrrp->iname); return false; } #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) cannot use VMAC if no interface specified", vrrp->iname); return false; } #endif } return true; } /* open a VRRP sending socket */ static int open_vrrp_send_socket(sa_family_t family, int proto, const interface_t *ifp, #ifdef _HAVE_VRF_ const interface_t *vrf_ifp, #endif const sockaddr_t *unicast_src) { int fd = -1; int val = 0; socklen_t len = sizeof(val); if (family != AF_INET && family != AF_INET6) { log_message(LOG_INFO, "cant open raw socket. unknown family=%d" , family); return -1; } /* Create and init socket descriptor */ fd = socket(family, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, proto); if (fd < 0) { log_message(LOG_INFO, "cant open raw socket. errno=%d", errno); return -1; } /* We are not receiving on the send socket, there is no * point allocating any buffers to it */ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, len)) log_message(LOG_INFO, "vrrp set send socket buffer size error %d", errno); #if !HAVE_DECL_IPV6_MULTICAST_ALL if (family == AF_INET) #endif if_setsockopt_mcast_all(family, &fd); if (family == AF_INET) { /* Set v4 related */ if_setsockopt_hdrincl(&fd); } else if (family == AF_INET6) { /* Set v6 related */ if_setsockopt_ipv6_checksum(&fd); if (!unicast_src) if_setsockopt_mcast_hops(family, &fd); } if (ifp) { if_setsockopt_bindtodevice(&fd, ifp); if (!unicast_src) { if_setsockopt_mcast_if(family, &fd, ifp); if_setsockopt_mcast_loop(family, &fd); } } #ifdef _HAVE_VRF_ else if (vrf_ifp) if_setsockopt_bindtodevice(&fd, vrf_ifp); #endif // TODO - do we want one send socket for all unicast, or per local unicast address to match recv socket. We could use the same socket for send and receive if_setsockopt_priority(&fd, family); if_setsockopt_no_receive(&fd); if (fd < 0) return -1; return fd; } /* open a VRRP socket and join the multicast group. */ static int open_vrrp_read_socket(sa_family_t family, int proto, const interface_t *ifp, #ifdef _HAVE_VRF_ const interface_t *vrf_ifp, #endif const sockaddr_t *mcast_daddr, const sockaddr_t *unicast_src, int rx_buf_size) { int fd = -1; int val = rx_buf_size; socklen_t len = sizeof(val); int on = 1; /* open the socket */ fd = socket(family, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, proto); if (fd < 0) { int err = errno; log_message(LOG_INFO, "cant open raw socket. errno=%d", err); return -1; } if (rx_buf_size) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, len)) log_message(LOG_INFO, "vrrp set receive socket buffer size error %d", errno); } /* Ensure no unwanted multicast packets are queued to this interface */ #if !HAVE_DECL_IPV6_MULTICAST_ALL if (family == AF_INET) #endif if_setsockopt_mcast_all(family, &fd); if (!unicast_src) { /* Join the VRRP multicast group */ /* coverity[forward_null] - ifp cannot be NULL if not unicast */ if_join_vrrp_group(family, &fd, ifp, mcast_daddr); /* Binding to the multicast address stops us receiving unicast * pkts when we are only interested in multicast. */ if ((family == AF_INET && bind(fd, PTR_CAST_CONST(struct sockaddr, mcast_daddr), sizeof(struct sockaddr_in))) || (family == AF_INET6 && bind(fd, PTR_CAST_CONST(struct sockaddr, mcast_daddr), sizeof(struct sockaddr_in6)))) log_message(LOG_INFO, "bind for multicast failed %d - %m", errno); } else { #ifdef _HAVE_VRF_ /* If the interface is in a VRF, we need to bind to the VRF device in order to bind to the address */ if (ifp && ifp->vrf_master_ifp) vrf_ifp = ifp->vrf_master_ifp; if (vrf_ifp && setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, vrf_ifp->ifname, strlen(vrf_ifp->ifname) + 1)) { log_message(LOG_INFO, "bind to VRF %s failed %d - %m", vrf_ifp->ifname, errno); close(fd); return -2; } #endif /* Allow binding even if the address doesn't exist yet */ #if !HAVE_DECL_IPV6_FREEBIND if (family == AF_INET6) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_TRANSPARENT, &on, sizeof on)) log_message(LOG_INFO, "IPV6_TRANSPARENT failed %d - %m", errno); } else #endif if (setsockopt(fd, family == AF_INET ? IPPROTO_IP : IPPROTO_IPV6, family == AF_INET ? IP_FREEBIND : IPV6_FREEBIND, &on, sizeof on)) log_message(LOG_INFO, "IP%s_FREEBIND failed %d - %m", family == AF_INET ? "" : "V6", errno); /* Bind to the local unicast address */ if (bind(fd, PTR_CAST_CONST(struct sockaddr, unicast_src), unicast_src->ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) { log_message(LOG_INFO, "bind unicast_src %s failed %d - %m", inet_sockaddrtos(unicast_src), errno); close(fd); return -2; } } if (family == AF_INET6) { /* IPv6 we need to receive the hop count as ancillary data */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof on)) log_message(LOG_INFO, "fd %d - set IPV6_RECVHOPLIMIT error %d (%m)", fd, errno); /* Receive the destination address as ancillary data to determine if packet multicast */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof on)) log_message(LOG_INFO, "fd %d - set IPV6_RECVPKTINFO error %d (%m)", fd, errno); } #ifdef _NETWORK_TIMESTAMP_ if (do_network_timestamp) { #if 0 int flags = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RX_SOFTWARE ; if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof(flags)) < 0) log_message(LOG_INFO, "ERROR: setsockopt %d SO_TIMESTAMPING", fd); if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)) < 0) log_message(LOG_INFO, "ERROR: setsockopt %d SO_TIMESTAMP", fd); #endif if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, &on, sizeof(on)) < 0) // This overrides SO_TIMESTAMP log_message(LOG_INFO, "ERROR: setsockopt %d SO_TIMESTAMPNS", fd); } #endif /* Need to bind read socket so only process packets for interface we're * interested in. * * This is applicable for both unicast and multicast operation as well as * IPv4 and IPv6. */ if (ifp) if_setsockopt_bindtodevice(&fd, ifp); #ifdef _HAVE_VRF_ else if (vrf_ifp) if_setsockopt_bindtodevice(&fd, vrf_ifp); #endif if (fd < 0) return -1; if (family == AF_INET6) { /* Let kernel calculate checksum. */ if_setsockopt_ipv6_checksum(&fd); } return fd; } void open_sockpool_socket(sock_t *sock) { vrrp_t *vrrp; bool already_fault; if (sock->unicast_src && sock->unicast_src->ss_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&PTR_CAST_CONST(struct sockaddr_in6, sock->unicast_src)->sin6_addr)) { /* For an IPv6 link local address, we need to set the ifindex */ /* coverity[deref_param] - since the address is IPv6 link local, sock->ifp != NULL */ PTR_CAST(struct sockaddr_in6, sock->unicast_src)->sin6_scope_id = sock->ifp->ifindex; } else if (sock->mcast_daddr && sock->mcast_daddr->ss_family == AF_INET6) PTR_CAST(struct sockaddr_in6, sock->mcast_daddr)->sin6_scope_id = sock->ifp->ifindex; sock->fd_in = open_vrrp_read_socket(sock->family, sock->proto, sock->ifp, #ifdef _HAVE_VRF_ sock->vrf_ifp, #endif sock->mcast_daddr, sock->unicast_src, sock->rx_buf_size); if (sock->fd_in == -2) { rb_for_each_entry(vrrp, &sock->rb_vrid, rb_vrid) { if (vrrp->state != VRRP_STATE_FAULT) log_message(LOG_INFO, "(%s): entering FAULT state (src address not configured)", vrrp->iname); already_fault = vrrp->flags_if_fault; down_instance(vrrp, VRRP_FAULT_FL_NO_SOURCE_IP); if (!already_fault) send_instance_notifies(vrrp); } sock->fd_in = -1; } if (sock->fd_in == -1) sock->fd_out = -1; else sock->fd_out = open_vrrp_send_socket(sock->family, sock->proto, sock->ifp, #ifdef _HAVE_VRF_ sock->vrf_ifp, #endif sock->unicast_src); } /* Try to find a VRRP instance */ static vrrp_t * __attribute__ ((pure)) vrrp_exist(vrrp_t *old_vrrp, list_head_t *l) { vrrp_t *vrrp; list_for_each_entry(vrrp, l, e_list) { if (vrrp->vrid != old_vrrp->vrid || vrrp->family != old_vrrp->family || #ifdef _HAVE_VRRP_VMAC_ vrrp->configured_ifp != old_vrrp->configured_ifp #else vrrp->ifp != old_vrrp->ifp #endif ) continue; /* Check for unicast match */ if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) != __test_bit(VRRP_FLAG_UNICAST, &old_vrrp->flags)) continue; if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { if (inet_sockaddrcmp(&old_vrrp->saddr, &vrrp->saddr)) continue; return vrrp; } #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) != __test_bit(VRRP_VMAC_BIT, &old_vrrp->flags)) return NULL; if (__test_bit(VRRP_VMAC_ADDR_BIT, &vrrp->flags) != __test_bit(VRRP_VMAC_ADDR_BIT, &old_vrrp->flags)) return NULL; #endif /* If multicast addresses are different, then don't match */ if (vrrp->family == AF_INET) { if (memcmp(&vrrp->mcast_daddr, &old_vrrp->mcast_daddr, sizeof (struct sockaddr_in))) return NULL; } else { /* We need to avoid comparing the sin6_scope_id, and the port and flowinfo fields are not used. */ if (memcmp(&PTR_CAST(struct sockaddr_in6, &vrrp->mcast_daddr)->sin6_addr, &PTR_CAST(struct sockaddr_in6, &old_vrrp->mcast_daddr)->sin6_addr, sizeof (struct in6_addr))) return NULL; } return vrrp; } return NULL; } /* handle terminate state phase 1 */ void restore_vrrp_interfaces(void) { vrrp_t *vrrp; /* Ensure any interfaces are in backup mode, * sending a priority 0 vrrp message */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { /* Remove VIPs/VROUTEs/VRULEs */ if (vrrp->state == VRRP_STATE_MAST) vrrp_restore_interface(vrrp, true, false); } } /* handle terminate state */ void shutdown_vrrp_instances(void) { vrrp_t *vrrp; #ifdef _HAVE_VRRP_VMAC_ list_head_t *vip_list; ip_address_t *vip; #endif #ifdef _HAVE_VRRP_VMAC_ restore_rp_filter(); #endif list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { /* We may not have an ifp if we are aborting at startup or are a unicast instance */ if (vrrp->ifp) { #ifdef _HAVE_VRRP_VMAC_ /* Remove VMAC. If we are shutting down due to a configuration * error, the VMACs may not be set up yet, and vrrp->ifp may * still point to the physical interface. */ if (vrrp->ifp->is_ours) netlink_link_del_vmac(vrrp); for (vip_list = &vrrp->vip; vip_list; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL) { list_for_each_entry(vip, vip_list, e_list) { if (!vip->ifp) continue; if (vrrp->ifp == vip->ifp) continue; if (!vip->ifp->is_ours) continue; if (!vip->ifp->ifindex) continue; /* For now create a dummy vrrp_instance to delete the VMAC i/f */ vrrp_t addr_vrrp = { .ifp = vip->ifp }; addr_vrrp.family = vip->ifa.ifa_family; addr_vrrp.iname = vrrp->iname; strcpy(addr_vrrp.vmac_ifname, vip->ifp->ifname); __set_bit(VRRP_VMAC_BIT, &addr_vrrp.flags); // This should be superfluous netlink_link_del_vmac(&addr_vrrp); vip->ifp->ifindex = 0; /* We are no longer running the kernel_netlink_monitor */ } } #endif if (vrrp->ifp->promote_secondaries) reset_promote_secondaries(vrrp->ifp); } } } static void add_vrrp_to_track_script(vrrp_t *vrrp, tracked_sc_t *sc) { vrrp_script_t *scr = sc->scr; tracking_obj_t *top; /* Is this script already tracking the vrrp instance directly? * For this to be the case, the script was added directly on the vrrp instance, * and now we are adding it for a sync group. */ list_for_each_entry(top, &scr->tracking_vrrp, e_list) { if (top->obj.vrrp == vrrp) { /* Update the weight appropriately. We will use the sync group's * weight unless the vrrp setting is unweighted. */ log_message(LOG_INFO, "(%s) track_script %s is configured on VRRP instance" " and sync group. Remove vrrp instance config" , vrrp->iname, scr->sname); if (top->weight) { top->weight = sc->weight; top->weight_multiplier = sc->weight_reverse ? -1 : 1; } return; } } PMALLOC(top); top->obj.vrrp = vrrp; top->weight = sc->weight; top->weight_multiplier = sc->weight_reverse ? -1 : 1; list_add_tail(&top->e_list, &scr->tracking_vrrp); } #ifdef _WITH_TRACK_PROCESS_ static void add_vrrp_to_track_process(vrrp_t *vrrp, tracked_process_t *tpr) { vrrp_tracked_process_t *proc = tpr->process; tracking_obj_t *top; /* Is this process already tracking the vrrp instance directly? * For this to be the case, the file was added directly on the vrrp instance, * and now we are adding it for a sync group. */ list_for_each_entry(top, &proc->tracking_vrrp, e_list) { if (top->obj.vrrp == vrrp) { /* Update the weight appropriately. We will use the sync group's * weight unless the vrrp setting is unweighted. */ log_message(LOG_INFO, "(%s) track_process %s is configured on VRRP instance" " and sync group. Remove vrrp instance config" , vrrp->iname, proc->pname); if (top->weight) top->weight = tpr->weight; return; } } PMALLOC(top); top->obj.vrrp = vrrp; top->weight = tpr->weight; top->weight_multiplier = tpr->weight_reverse ? -1 : 1; list_add_tail(&top->e_list, &proc->tracking_vrrp); } #endif #ifdef _WITH_BFD_ static void add_vrrp_to_track_bfd(vrrp_t *vrrp, tracked_bfd_t *tbfd) { vrrp_tracked_bfd_t *bfd = tbfd->bfd; tracking_obj_t *top; /* Is this bfd already tracking the vrrp instance directly? * For this to be the case, the bfd was added directly on the vrrp instance, * and now we are adding it for a sync group. */ list_for_each_entry(top, &bfd->tracking_vrrp, e_list) { if (top->obj.vrrp == vrrp) { /* Update the weight appropriately. We will use the sync group's * weight unless the vrrp setting is unweighted. */ log_message(LOG_INFO, "(%s) track_bfd %s is configured on VRRP instance" " and sync group. Remove vrrp instance config" , vrrp->iname, bfd->bname); if (top->weight) { top->weight = tbfd->weight; top->weight_multiplier = tbfd->weight_reverse ? -1 : 1; } return; } } PMALLOC(top); top->obj.vrrp = vrrp; top->weight = tbfd->weight; top->weight_multiplier = tbfd->weight_reverse ? -1 : 1; list_add_tail(&top->e_list, &bfd->tracking_vrrp); } #endif #ifdef _HAVE_VRRP_VMAC_ static interface_t * create_vmac_name(const char *prefix, uint8_t vrid, int family) { char ifname[IFNAMSIZ]; interface_t *ifp; unsigned short num=0; int len; bool name_in_use; vrrp_t *vrrp; len = snprintf(ifname, IFNAMSIZ, "%s.%d", prefix, vrid); if (len >= IFNAMSIZ) snprintf(ifname, IFNAMSIZ, "%.*s.%d", (int)strlen(prefix) - (len - IFNAMSIZ) - 1, prefix, vrid); while (true) { /* If there is no VMAC with the name and no existing * interface with the name, we can use it. * It we are using dynamic interfaces, the interface entry * may have been created by the configuration, but in that * case the ifindex will be 0. */ // This was wrong if dynamic interfaces and an interface has already been specified but it doesn't exist /* Check no vrrp instance is using this name for a VMAC */ name_in_use = false; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!strcmp(ifname, vrrp->vmac_ifname)) { name_in_use = true; break; } } if (!name_in_use && (ifp = if_get_by_ifname(ifname, IF_CREATE_NOT_EXIST))) return ifp; /* For IPv6 try vrrp6 as second attempt */ if (family == AF_INET6) { if (num == 0) num = 6; else if (num == 6) num = 1; else if (++num == 6) num++; } else num++; len = snprintf(ifname, IFNAMSIZ, "%s%d.%d", prefix, num, vrid); if (len >= IFNAMSIZ) snprintf(ifname, IFNAMSIZ, "%.*s%d.%d", (int)strlen(prefix) - (len - IFNAMSIZ) - 1, prefix, num, vrid); } } #endif /* complete vrrp structure */ static bool vrrp_complete_instance(vrrp_t * vrrp) { #ifdef _HAVE_VRRP_VMAC_ interface_t *ifp = NULL; const char *if_type; interface_t *base_ifp; interface_t *old_interface = NULL; bool if_sorted; bool use_extra_if = false; bool use_extra_vmac = false; bool old_vmac_deleted = false; vrrp_t *old_vrrp; #endif list_head_t *vip_list; ip_address_t *ip_addr, *ip_addr_tmp; size_t hdr_len; size_t max_addr; size_t i; bool interface_already_existed = false; tracked_sc_t *sc, *sc_tmp; tracked_if_t *tip, *tip_tmp; tracked_file_monitor_t *tfl, *tfl_tmp; #ifdef _WITH_TRACK_PROCESS_ tracked_process_t *tpr; #endif #ifdef _WITH_BFD_ tracked_bfd_t *tbfd, *tbfd_tmp; #endif ip_route_t *route; ip_rule_t *rule; if (vrrp->strict_mode == PARAMETER_UNSET) vrrp->strict_mode = global_data->vrrp_strict; if (vrrp->family == AF_INET6) { if (vrrp->version == VRRP_VERSION_2 && vrrp->strict_mode) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) cannot use IPv6 with VRRP version 2;" " setting version 3" , vrrp->iname); vrrp->version = VRRP_VERSION_3; } else if (!vrrp->version) vrrp->version = VRRP_VERSION_3; } /* Default to IPv4. This can only happen if no VIPs are specified. */ if (vrrp->family == AF_UNSPEC) vrrp->family = AF_INET; if (vrrp->family == AF_INET) have_ipv4_instance = true; else have_ipv6_instance = true; if (vrrp->version == 0) { if (vrrp->family == AF_INET6) vrrp->version = VRRP_VERSION_3; else vrrp->version = global_data->vrrp_version; } /* If necessary, set the default TTL value. For IPv6 the default * is to let the kernel set the default value. */ if (vrrp->family == AF_INET && vrrp->ttl == -1) vrrp->ttl = VRRP_IP_TTL; /* If no priority has been set, derive it from the initial state */ if (vrrp->base_priority == 0) { if (vrrp->wantstate == VRRP_STATE_MAST) vrrp->base_priority = VRRP_PRIO_OWNER; else vrrp->base_priority = VRRP_PRIO_DFL; } /* If no initial state has been set, derive it from the priority */ if (vrrp->wantstate == VRRP_STATE_INIT) vrrp->wantstate = (vrrp->base_priority == VRRP_PRIO_OWNER ? VRRP_STATE_MAST : VRRP_STATE_BACK); else if (vrrp->strict_mode && ((vrrp->wantstate == VRRP_STATE_MAST) != (vrrp->base_priority == VRRP_PRIO_OWNER))) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) State MASTER must match being address owner" , vrrp->iname); vrrp->wantstate = (vrrp->base_priority == VRRP_PRIO_OWNER ? VRRP_STATE_MAST : VRRP_STATE_BACK); } #ifdef _WITH_VRRP_AUTH_ if (vrrp->strict_mode && vrrp->auth_type != VRRP_AUTH_NONE) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Strict mode does not support authentication." " Ignoring." , vrrp->iname); vrrp->auth_type = VRRP_AUTH_NONE; } else if (vrrp->version == VRRP_VERSION_3 && vrrp->auth_type != VRRP_AUTH_NONE) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRP version 3 does not support authentication." " Ignoring." , vrrp->iname); vrrp->auth_type = VRRP_AUTH_NONE; } else if (vrrp->auth_type != VRRP_AUTH_NONE && !vrrp->auth_data[0]) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Authentication specified but no password given." " Ignoring" , vrrp->iname); vrrp->auth_type = VRRP_AUTH_NONE; } else if (vrrp->family == AF_INET6 && vrrp->auth_type == VRRP_AUTH_AH) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot use AH authentication with IPv6." " ignoring" , vrrp->iname); vrrp->auth_type = VRRP_AUTH_NONE; } else if (vrrp->auth_type == VRRP_AUTH_AH && vrrp->wantstate == VRRP_STATE_MAST && vrrp->base_priority != VRRP_PRIO_OWNER) { /* We need to have received an advert to get the AH sequence no before taking over, if possible */ report_config_error(CONFIG_GENERAL_ERROR, "(%s) Initial state master is incompatible with AH" " authentication - clearing" , vrrp->iname); vrrp->wantstate = VRRP_STATE_BACK; } #endif /* unicast peers aren't allowed in strict mode if the interface supports multicast */ if (vrrp->strict_mode && __test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) && vrrp->ifp && vrrp->ifp->ifindex && (vrrp->ifp->ifi_flags & IFF_MULTICAST)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Unicast peers are not supported in strict mode" , vrrp->iname); return false; } if (!vrrp->ifp) { if (!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): Unicast instances must have unicast_src_ip or interface", vrrp->iname); return false; } /* For IPv6 unicast, we cannot have no interface and a link local or no src ip address */ if (vrrp->family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&(PTR_CAST_CONST(struct sockaddr_in6, &vrrp->saddr)->sin6_addr))) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Non link-local address required if interface omitted" , vrrp->iname); return false; } } /* Strictly, specifying any "unicast" keyword and not having any unicast peers * is not valid. However, if no unicast peers are specified, then up to v2.2.4 * this has always been treated as ignore unicast and use multicast. */ if (__test_bit(VRRP_FLAG_UNICAST_CONFIGURED, &vrrp->flags)) { if (!list_empty(&vrrp->unicast_peer)) __set_bit(VRRP_FLAG_UNICAST, &vrrp->flags); else if (__test_bit(VRRP_FLAG_UNICAST_FAULT_NO_PEERS, &vrrp->flags)) { /* We go to fault state to stop defaulting to multicast. We * cannot operate in unicast mode without any peers. */ log_message(LOG_INFO, "(%s) Cannot use unicast without any peers - going to fault state", vrrp->iname); vrrp->num_config_faults++; } else { /* Deprecated after v2.2.4 */ report_config_error(CONFIG_DEPRECATED, "(%s) A unicast keyword has been specified without any unicast peers. Defaulting to multicast. This usage is deprecated - please update your configuration.", vrrp->iname); } } #ifdef _HAVE_VRRP_VMAC_ if (vrrp->strict_mode && __test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): cannot specify MAC address with strict mode - clearing", vrrp->iname); __clear_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags); } if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && __test_bit(VRRP_VMAC_MAC_USE_VRID, &vrrp->flags)) vrrp->ll_addr[ETH_ALEN - 1] = vrrp->vrid; /* Check that the underlying interface type is Ethernet if using a VMAC */ if ((__test_bit(VRRP_VMAC_BIT, &vrrp->flags) #ifdef _HAVE_VRRP_IPVLAN_ || __test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) #endif ) && vrrp->ifp && vrrp->ifp->ifindex && vrrp->ifp->hw_type != ARPHRD_ETHER) { vrrp->flags = 0; report_config_error(CONFIG_GENERAL_ERROR, "(%s): vmacs are only supported on Ethernet type interfaces" , vrrp->iname); vrrp->num_config_faults++; /* Stop the vrrp instance running */ } #endif /* If the interface doesn't support multicast, then we need to use unicast */ if (vrrp->ifp && vrrp->ifp->ifindex && !(vrrp->ifp->ifi_flags & IFF_MULTICAST) && !__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) interface %s does not support multicast," " specify unicast peers - disabling" , vrrp->iname, vrrp->ifp->ifname); vrrp->num_config_faults++; /* Stop the vrrp instance running */ } /* Warn if ARP not supported on interface */ if (__test_bit(LOG_DETAIL_BIT, &debug) && vrrp->ifp && vrrp->ifp->ifindex && (vrrp->ifp->ifi_flags & IFF_NOARP) && !(vrrp->ifp->ifi_flags & IFF_POINTOPOINT)) report_config_error(CONFIG_GENERAL_ERROR, "(%s) disabling ARP since interface does not support it" , vrrp->iname); /* If the addresses are IPv6, then the first one must be link local */ if (vrrp->family == AF_INET6 && !__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) && !list_empty(&vrrp->vip)) { ip_addr = list_first_entry(&vrrp->vip, ip_address_t, e_list); if (!IN6_IS_ADDR_LINKLOCAL(&ip_addr->u.sin6_addr)) { if (vrrp->strict_mode) report_config_error(CONFIG_GENERAL_ERROR, "(%s) the first IPv6 VIP address must be link local" , vrrp->iname); else log_message(LOG_INFO, "(%s) the first IPv6 VIP address should be link local" , vrrp->iname); } } #ifdef _HAVE_VRF_ /* Check that if vrf is specified, we are using unicast and no interface has been specified */ if (vrrp->vrf_ifp && (vrrp->ifp || !__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags))) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) vrf can only be specified with%s - clearing", vrrp->iname, vrrp->ifp ? "out interface" : " unicast"); vrrp->vrf_ifp = NULL; } #endif /* Check we can fit the VIPs into a packet */ if (vrrp->family == AF_INET) { hdr_len = sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(vrrphdr_t); if (vrrp->version == VRRP_VERSION_2) { hdr_len += VRRP_AUTH_LEN; #ifdef _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) hdr_len += sizeof(ipsec_ah_t); #endif } max_addr = ((vrrp->ifp ? vrrp->ifp->mtu : DEFAULT_MTU) - hdr_len) / sizeof(struct in_addr); } else { hdr_len = sizeof(struct ether_header) + sizeof(struct ip6_hdr) + sizeof(vrrphdr_t); max_addr = ((vrrp->ifp ? vrrp->ifp->mtu : DEFAULT_MTU) - hdr_len) / sizeof(struct in6_addr); } /* Count IP addrs field is 8 bits wide, giving a maximum address count of 255 */ if (max_addr > VRRP_MAX_ADDR) max_addr = VRRP_MAX_ADDR; /* Move any extra addresses to be evips. We won't advertise them, but at least we can respond to them */ if (!list_empty(&vrrp->vip) && vrrp->vip_cnt > max_addr) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Number of VIPs (%u) exceeds maximum/space" " available in packet (max %zu addresses)" " - excess moved to eVIPs" , vrrp->iname , vrrp->vip_cnt, max_addr); i = 0; list_for_each_entry_safe(ip_addr, ip_addr_tmp, &vrrp->vip, e_list) { if (++i <= max_addr) continue; list_del_init(&ip_addr->e_list); list_add_tail(&ip_addr->e_list, &vrrp->evip); vrrp->vip_cnt--; } } if (vrrp->base_priority == VRRP_PRIO_OWNER && __test_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) nopreempt is incompatible with priority %d." " resetting nopreempt" , vrrp->iname, VRRP_PRIO_OWNER); __clear_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags); } vrrp->effective_priority = vrrp->base_priority; vrrp->total_priority = vrrp->base_priority; if (vrrp->wantstate == VRRP_STATE_MAST) { if (__test_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Warning - nopreempt will not work" " with initial state MASTER - clearing" , vrrp->iname); __clear_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags); } if (vrrp->preempt_delay) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Warning - preempt delay will not work" " with initial state MASTER - clearing" , vrrp->iname); vrrp->preempt_delay = false; } } if (vrrp->preempt_delay) { if (vrrp->strict_mode) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) preempt_delay is incompatible with" " strict mode - resetting" , vrrp->iname); vrrp->preempt_delay = 0; } if (__test_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) preempt_delay is incompatible with" " nopreempt mode - resetting" , vrrp->iname); vrrp->preempt_delay = 0; } } if (vrrp->down_timer_adverts != VRRP_DOWN_TIMER_ADVERTS && vrrp->strict_mode) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) down_timer_adverts is incompatible with" " strict mode - resetting" , vrrp->iname); vrrp->down_timer_adverts = VRRP_DOWN_TIMER_ADVERTS; } if (!__test_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags) && vrrp->highest_other_priority) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) expired_timer_backup requires nopreempt - resetting" , vrrp->iname); vrrp->highest_other_priority = 0; } vrrp->state = VRRP_STATE_INIT; #ifdef _WITH_SNMP_VRRP_ vrrp->configured_state = vrrp->wantstate; #endif #ifdef _WITH_FIREWALL_ /* Set default for accept mode if not specified. If we are running in strict mode, * default is to disable accept mode unless the instance is the address owner * (priority 255), otherwise default is to enable it. * At some point we might want to change this to make non accept_mode the default, * to comply with the RFCs. */ if (vrrp->accept == PARAMETER_UNSET) vrrp->accept = (vrrp->base_priority == VRRP_PRIO_OWNER) ? true : !vrrp->strict_mode; if (vrrp->accept && vrrp->base_priority != VRRP_PRIO_OWNER && vrrp->strict_mode && vrrp->version == VRRP_VERSION_2) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) warning - accept mode for VRRP version 2" " does not comply with RFC3768 - resetting" , vrrp->iname); vrrp->accept = false; } if (!vrrp->accept #ifdef _HAVE_VRRP_VMAC_ || __test_bit(VRRP_VMAC_BIT, &vrrp->flags) #endif ) { #ifdef _WITH_IPTABLES_ if (!global_data->vrrp_iptables_inchain) #endif { #ifndef _WITH_NFTABLES_ log_message(LOG_INFO, "Warning - firewall needed due to use_vmac or no_accept/strict" " but not configured"); #else if (!global_data->vrrp_nf_table_name) { log_message(LOG_INFO, "use_vmac or no_accept/strict specified," " but no firewall configured - using nftables"); global_data->vrrp_nf_table_name = STRDUP(DEFAULT_NFTABLES_TABLE); } #endif } } #endif /* Set the multicast address if appropriate */ if (!__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) && vrrp->mcast_daddr.ss_family == AF_UNSPEC) { if (vrrp->family == AF_INET) *PTR_CAST(struct sockaddr_in, &vrrp->mcast_daddr) = global_data->vrrp_mcast_group4; else *PTR_CAST(struct sockaddr_in6, &vrrp->mcast_daddr) = global_data->vrrp_mcast_group6; } else if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) && vrrp->mcast_daddr.ss_family != AF_UNSPEC) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) multicast destination specified with unicast - ignoring", vrrp->iname); vrrp->mcast_daddr.ss_family = AF_UNSPEC; } if (vrrp->garp_lower_prio_rep == PARAMETER_UNSET) vrrp->garp_lower_prio_rep = vrrp->strict_mode ? 0 : global_data->vrrp_garp_lower_prio_rep; else if (vrrp->strict_mode && vrrp->garp_lower_prio_rep) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Strict mode requires no repeat garps." " resetting" , vrrp->iname); vrrp->garp_lower_prio_rep = 0; } if (vrrp->garp_lower_prio_delay == PARAMETER_UNSET) vrrp->garp_lower_prio_delay = vrrp->strict_mode ? 0 : global_data->vrrp_garp_lower_prio_delay; else if (vrrp->strict_mode && vrrp->garp_lower_prio_delay) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Strict mode requires no repeat garp delay." " resetting" , vrrp->iname); vrrp->garp_lower_prio_delay = 0; } if (vrrp->lower_prio_no_advert == PARAMETER_UNSET) vrrp->lower_prio_no_advert = vrrp->strict_mode ? true : global_data->vrrp_lower_prio_no_advert; else if (vrrp->strict_mode && !vrrp->lower_prio_no_advert) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Strict mode requires no lower priority advert." " resetting" , vrrp->iname); vrrp->lower_prio_no_advert = true; } if (vrrp->higher_prio_send_advert == PARAMETER_UNSET) vrrp->higher_prio_send_advert = vrrp->strict_mode ? false : global_data->vrrp_higher_prio_send_advert; else if (vrrp->strict_mode && vrrp->higher_prio_send_advert) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) strict mode requires higher_prio_send_advert" " to be clear. resetting" , vrrp->iname); vrrp->higher_prio_send_advert = false; } if (vrrp->owner_ignore_adverts == true && vrrp->base_priority != VRRP_PRIO_OWNER) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) ignoring owner_ignore_adverts since priority is not %d", vrrp->iname, VRRP_PRIO_OWNER); vrrp->owner_ignore_adverts = false; } else if (vrrp->owner_ignore_adverts == PARAMETER_UNSET) { if (vrrp->base_priority == VRRP_PRIO_OWNER) { if (vrrp->strict_mode) vrrp->owner_ignore_adverts = true; else vrrp->owner_ignore_adverts = global_data->vrrp_owner_ignore_adverts; } else vrrp->owner_ignore_adverts = false; } else if (vrrp->base_priority == VRRP_PRIO_OWNER && !vrrp->owner_ignore_adverts) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) strict_mode with priority %d requires owner_ignore_adverts", vrrp->iname, VRRP_PRIO_OWNER); vrrp->owner_ignore_adverts = true; } if (vrrp->smtp_alert == -1) { if (global_data->smtp_alert_vrrp != -1) vrrp->smtp_alert = global_data->smtp_alert_vrrp; else if (global_data->smtp_alert != -1) vrrp->smtp_alert = global_data->smtp_alert; else vrrp->smtp_alert = false; } if (vrrp->notify_priority_changes == -1) { if (vrrp->sync && vrrp->sync->notify_priority_changes != -1) vrrp->notify_priority_changes = vrrp->sync->notify_priority_changes; else vrrp->notify_priority_changes = global_data->vrrp_notify_priority_changes; } /* Check that the advertisement interval is valid */ if (!vrrp->adver_int) vrrp->adver_int = VRRP_ADVER_DFL * TIMER_HZ; if (vrrp->version == VRRP_VERSION_2) { if (vrrp->adver_int >= (1<<8) * TIMER_HZ) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRPv2 advertisement interval %.2fs" " is out of range. Must be less than %ds." " Setting to %ds" , vrrp->iname , vrrp->adver_int / TIMER_HZ_DOUBLE , 1<<8, (1<<8) - 1); vrrp->adver_int = ((1<<8) - 1) * TIMER_HZ; } else if (vrrp->adver_int % TIMER_HZ) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRPv2 advertisement interval %fs" " must be an integer. rounding" , vrrp->iname , vrrp->adver_int / TIMER_HZ_DOUBLE); vrrp->adver_int = vrrp->adver_int + (TIMER_HZ / 2); vrrp->adver_int -= vrrp->adver_int % TIMER_HZ; if (vrrp->adver_int == 0) vrrp->adver_int = TIMER_HZ; } } else { if (vrrp->adver_int >= (1<<12) * TIMER_CENTI_HZ) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRPv3 advertisement interval %.2fs" " is out of range. Must be less than %.2fs." " Setting to %.2fs" , vrrp->iname , vrrp->adver_int / TIMER_HZ_DOUBLE , (double)(1<<12) / 100, (double)((1<<12) - 1) / 100); vrrp->adver_int = ((1<<12) - 1) * TIMER_CENTI_HZ; } else if (vrrp->adver_int % TIMER_CENTI_HZ) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRPv3 advertisement interval %fs" " must be in units of 10ms - rounding" , vrrp->iname , vrrp->adver_int / TIMER_HZ_DOUBLE); vrrp->adver_int = vrrp->adver_int + (TIMER_CENTI_HZ / 2); vrrp->adver_int -= vrrp->adver_int % TIMER_CENTI_HZ; /* Ensure don't round outside [0.01,40.95] */ if (vrrp->adver_int == 0) vrrp->adver_int = TIMER_CENTI_HZ; else if (vrrp->adver_int == (1<<12) * TIMER_CENTI_HZ) vrrp->adver_int = ((1<<12) - 1) * TIMER_CENTI_HZ; } } vrrp->master_adver_int = vrrp->adver_int; #ifdef _WITH_LINKBEAT_ /* Set linkbeat polling on interface if wanted */ if (vrrp->ifp && (__test_bit(VRRP_FLAG_LINKBEAT_USE_POLLING, &vrrp->flags) || global_data->linkbeat_use_polling)) vrrp->ifp->linkbeat_use_polling = true; #endif /* Check that the interface up/down timers do not exceed twice, or * more strictly (vrrp->down_timer_adverts - 1) * the * advert interval. We also need to adjust these if another VRRPv3 * master instance has a lower advert interval. */ if (vrrp->down_timer_adverts == 1) { if (IF_BASE_IFP(vrrp->ifp)->up_debounce_timer || IF_BASE_IFP(vrrp->ifp)->down_debounce_timer) { log_message(LOG_INFO, "%s: cannot use debounce timers with down_timer_adverts = 1 - resetting", vrrp->iname); IF_BASE_IFP(vrrp->ifp)->up_debounce_timer = 0; IF_BASE_IFP(vrrp->ifp)->down_debounce_timer = 0; #ifdef _HAVE_VRRP_VMAC_ if (vrrp->ifp != vrrp->ifp->base_ifp) { vrrp->ifp->up_debounce_timer = 0; vrrp->ifp->down_debounce_timer = 0; } #endif } } else if (check_debounce_timers(vrrp, vrrp->adver_int)) log_message(LOG_INFO, "%s: interface %s debounce timer(s) not less that %u * advert_int - resetting", vrrp->iname, vrrp->ifp->ifname, vrrp->down_timer_adverts - 1); /* Clear track_saddr if no saddr specified */ if (!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags)) __clear_bit(VRRP_FLAG_TRACK_SADDR, &vrrp->flags); #ifdef _HAVE_VRRP_VMAC_ /* Set a default interface name for the vmac if needed */ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) #ifdef _HAVE_VRRP_IPVLAN_ || __test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) #endif ) { /* The same vrid can be used for both IPv4 and IPv6, and also on multiple underlying * interfaces. */ if_type = #ifdef _HAVE_VRRP_IPVLAN_ __test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) ? "IPVLAN" : #endif "VMAC"; /* Look to see if an existing interface matches. If so, use that name */ list_head_t *ifq = get_interface_queue(); list_for_each_entry(ifp, ifq, e_list) { /* Check if this interface could be the macvlan/ipvlan for this vrrp */ if (ifp->ifindex && (ifp->base_ifp == vrrp->configured_ifp->base_ifp #ifdef HAVE_IFLA_LINK_NETNSID || (ifp == ifp->base_ifp && ifp->base_netns_id != -1 && vrrp->configured_ifp->base_netns_id == ifp->base_netns_id && vrrp->configured_ifp->base_ifindex == ifp->base_ifindex) #endif ) && ((__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && ifp->vmac_type == MACVLAN_MODE_PRIVATE && ((__test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags) && !memcmp(ifp->hw_addr, vrrp->ll_addr, sizeof(ll_addr))) || (!__test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags) && !memcmp(ifp->hw_addr, ll_addr, sizeof(ll_addr) - 2) && ((vrrp->family == AF_INET && ifp->hw_addr[sizeof(ll_addr) - 2] == 0x01) || (vrrp->family == AF_INET6 && ifp->hw_addr[sizeof(ll_addr) - 2] == 0x02)) && ifp->hw_addr[sizeof(ll_addr) - 1] == vrrp->vrid))) #ifdef _HAVE_VRRP_IPVLAN_ || /* We should probably check if any VIPs match for IPv6 when no i/f name or address configured */ (__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) && ifp->if_type == IF_TYPE_IPVLAN && /* coverity[mixed_enums] */ ifp->vmac_type == IPVLAN_MODE_L2 && #if HAVE_DECL_IFLA_IPVLAN_FLAGS ifp->ipvlan_flags == vrrp->ipvlan_type && #endif !(vrrp->family == AF_INET6 && !vrrp->vmac_ifname[0] && !vrrp->ipvlan_addr) && (!vrrp->vmac_ifname[0] || !strcmp(vrrp->vmac_ifname, ifp->ifname)) && (!vrrp->ipvlan_addr || (vrrp->ipvlan_addr->ifa.ifa_family == AF_INET && !inet_inaddrcmp(AF_INET, &vrrp->ipvlan_addr->u.sin.sin_addr.s_addr, &ifp->sin_addr.s_addr)) || (vrrp->ipvlan_addr->ifa.ifa_family == AF_INET6 && !inet_inaddrcmp(AF_INET6, &vrrp->ipvlan_addr->u.sin6_addr, &ifp->sin6_addr)))) #endif )) { log_message(LOG_INFO, "(%s) Found matching interface %s", vrrp->iname, ifp->ifname); if (vrrp->vmac_ifname[0] && strcmp(vrrp->vmac_ifname, ifp->ifname)) { if (reload && ifp->is_ours) { list_for_each_entry(old_vrrp, &old_vrrp_data->vrrp, e_list) { if (old_vrrp->ifp->ifindex == ifp->ifindex) { log_message(LOG_INFO, "(%s) Deleting old VMAC interface %s", vrrp->iname, ifp->ifname); netlink_link_del_vmac(old_vrrp); old_vmac_deleted = true; break; } } } if (!old_vmac_deleted) log_message(LOG_INFO, "(%s) vmac name mismatch %s <=> %s." " changing to %s." , vrrp->iname , vrrp->vmac_ifname , ifp->ifname, ifp->ifname); } if (!old_vmac_deleted) { strcpy(vrrp->vmac_ifname, ifp->ifname); vrrp->ifp = ifp; __set_bit(VRRP_VMAC_UP_BIT, &vrrp->flags); ifp->is_ours = true; /* The interface existed, so it may have config set on it */ interface_already_existed = true; } break; } } if (!interface_already_existed && vrrp->vmac_ifname[0] && (ifp = if_get_by_ifname(vrrp->vmac_ifname, IF_NO_CREATE)) && ifp->ifindex) { /* An interface with the same name exists, but it doesn't match */ if (IS_MAC_IP_VLAN(ifp)) { log_message(LOG_INFO, "(%s) %s %s already exists but is incompatible." " It will be deleted/updated" , vrrp->iname, if_type, vrrp->vmac_ifname); if (ifp->base_ifp->ifindex != vrrp->configured_ifp->ifindex) old_interface = ifp; } else { report_config_error(CONFIG_GENERAL_ERROR, "(%s) %s interface name %s" " already exists as a non %s" " interface. ignoring configured name" , vrrp->iname , if_type, vrrp->vmac_ifname, if_type); vrrp->vmac_ifname[0] = 0; } } /* No interface found, find an unused name */ if (!vrrp->vmac_ifname[0]) { ifp = create_vmac_name(global_data->vmac_prefix ? global_data->vmac_prefix : "vrrp", vrrp->vrid, vrrp->family); /* We've found a unique name */ strcpy_safe(vrrp->vmac_ifname, ifp->ifname); } else if (!interface_already_existed) ifp = if_get_by_ifname(vrrp->vmac_ifname, IF_CREATE_ALWAYS); if (!interface_already_existed) { ifp->base_ifp = vrrp->ifp; vrrp->ifp = ifp; } if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags)) { if (vrrp->strict_mode && __test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) xmit_base is incompatible" " with strict mode - resetting" , vrrp->iname); __clear_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags); } /* If vmac_xmit_base is changing, add or remove the VMAC's * link local address as appropriate. */ if (interface_already_existed && vrrp->family == AF_INET6) { if (!__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) && IN6_IS_ADDR_UNSPECIFIED(&ifp->sin6_addr)) { set_link_local_address(vrrp); } else if (__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) && !IN6_IS_ADDR_UNSPECIFIED(&ifp->sin6_addr)) { del_link_local_address(ifp); } } } if (__test_bit(VRRP_FLAG_PROMOTE_SECONDARIES, &vrrp->flags) && (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) #ifdef _HAVE_VRRP_IPVLAN_ || __test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) #endif )) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) promote_secondaries is automatically" " set for vmacs - ignoring" , vrrp->iname); __clear_bit(VRRP_FLAG_PROMOTE_SECONDARIES, &vrrp->flags); } /* The VMAC uses the same up/down debounce delays as its parent interface */ vrrp->ifp->down_debounce_timer = vrrp->ifp->base_ifp->down_debounce_timer; vrrp->ifp->up_debounce_timer = vrrp->ifp->base_ifp->up_debounce_timer; } else #endif { /* We are using a "physical" interface, so it may have configuration on it * left over from a previous run. */ interface_already_existed = true; } #ifdef _HAVE_VRRP_IPVLAN_ if (__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags)) { if (vrrp->family == AF_INET && !vrrp->ipvlan_addr) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) IPv4 ipvlan requires a source ip address" " to be configured. setting instance to fault state" , vrrp->iname); vrrp->num_config_faults++; } else if (vrrp->ipvlan_addr) { if (vrrp->family != vrrp->ipvlan_addr->ifa.ifa_family) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) IPv4 ipvlan address family" " does not match instance." " setting instance to fault state" , vrrp->iname); vrrp->num_config_faults++; } else vrrp->ipvlan_addr->ifp = vrrp->ifp; } } #endif /* Add us to the interfaces we are tracking */ if (vrrp->ifp) { list_for_each_entry_safe(tip, tip_tmp, &vrrp->track_ifp, e_list) { /* Check the configuration doesn't explicitly state to track our own interface */ if (tip->ifp == IF_BASE_IFP(vrrp->ifp)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Ignoring track_interface %s" " since own interface" , vrrp->iname , IF_BASE_IFP(vrrp->ifp)->ifname); free_track_if(tip); } else add_vrrp_to_interface(vrrp, tip->ifp, tip->weight, tip->weight_reverse, false, TRACK_IF); } /* Add this instance to the physical interface and vice versa */ add_vrrp_to_interface(vrrp, VRRP_CONFIGURED_IFP(vrrp), __test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags) ? VRRP_NOT_TRACK_IF : 0, false, true, TRACK_VRRP); } #ifdef _HAVE_VRRP_VMAC_ /* If the interface is configured onto a VMAC/IPVLAN interface, we want to track * the underlying interface too */ if (vrrp->configured_ifp && vrrp->configured_ifp != vrrp->configured_ifp->base_ifp && vrrp->configured_ifp->base_ifp) add_vrrp_to_interface(vrrp, vrrp->configured_ifp->base_ifp, __test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags) ? VRRP_NOT_TRACK_IF : 0, false, true, TRACK_VRRP_DYNAMIC); if (__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) && !__test_bit(VRRP_VMAC_BIT, &vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) vmac_xmit_base is only valid with a vmac" , vrrp->iname); __clear_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags); } if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) #ifdef _HAVE_VRRP_IPVLAN_ || __test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) #endif ) { /* We need to know if we have eVIPs of the other address family */ list_for_each_entry(ip_addr, &vrrp->evip, e_list) { if (ip_addr->ifa.ifa_family != vrrp->family) { __set_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags); break; } } /* Create the interface if it doesn't already exist and * the underlying interface does exist */ if (vrrp->ifp->base_ifp->ifindex && !__test_bit(VRRP_VMAC_UP_BIT, &vrrp->flags) && !__test_bit(CONFIG_TEST_BIT, &debug)) { #ifdef _HAVE_VRRP_IPVLAN_ if (__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags)) { /* coverity[var_deref_model] - vrrp->configured_ifp is not NULL for IPVLAN */ netlink_link_add_ipvlan(vrrp); } else #endif { /* coverity[var_deref_model] - vrrp->configured_ifp is not NULL for VMAC */ netlink_link_add_vmac(vrrp, old_interface); } } else if (old_interface) netlink_link_del_vmac(vrrp); if (vrrp->ifp->base_ifp->ifindex && !__test_bit(VRRP_VMAC_UP_BIT, &vrrp->flags) && __test_bit(CONFIG_TEST_BIT, &debug)) { #ifdef _HAVE_VRRP_IPVLAN_ if (!__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags)) #endif { ifp = if_get_by_vmac(vrrp->vrid, vrrp->family, vrrp->ifp->base_ifp, __test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags) ? vrrp->ll_addr : NULL); if (ifp) vrrp->ifp = ifp; else { ifp = if_get_by_ifname(vrrp->vmac_ifname, IF_CREATE_ALWAYS); ifp->is_ours = true; ifp->if_type = IF_TYPE_MACVLAN; ifp->base_ifp = vrrp->ifp; if (__test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags)) memcpy(ifp->hw_addr, vrrp->ll_addr, sizeof(vrrp->ll_addr)); else { ifp->hw_addr[0] = ll_addr[0]; ifp->hw_addr[1] = ll_addr[1]; ifp->hw_addr[2] = ll_addr[2]; ifp->hw_addr[3] = ll_addr[3]; ifp->hw_addr[4] = vrrp->family == AF_INET ? 0x01 : 0x02; ifp->hw_addr[5] = vrrp->vrid; } vrrp->ifp = ifp; } } } /* Add this instance to the vmac interface */ add_vrrp_to_interface(vrrp, vrrp->ifp, __test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags) ? VRRP_NOT_TRACK_IF : 0, false, true, TRACK_VRRP); } #endif /* We need to set the scope_id for link local and node local multicast addresses, but we set it * for all IPv6 multicast addresses anyway. */ if (vrrp->mcast_daddr.ss_family == AF_INET6) PTR_CAST(struct sockaddr_in6, &vrrp->mcast_daddr)->sin6_scope_id = #ifdef _HAVE_VRRP_VMAC_ __test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) ? vrrp->ifp->base_ifp->ifindex : #endif vrrp->ifp->ifindex; /* See if we need to enable the firewall */ //TODO = we have a problem since SNMP may change accept mode //it can also change priority #ifdef _WITH_FIREWALL_ if ((vrrp->base_priority != VRRP_PRIO_OWNER && !vrrp->accept) #ifdef _HAVE_VRRP_VMAC_ || __test_bit(VRRP_VMAC_BIT, &vrrp->flags) #endif ) { bool have_firewall = false; #ifdef _WITH_IPTABLES_ if (global_data->vrrp_iptables_inchain) have_firewall = true; #endif #ifdef _WITH_NFTABLES_ if (global_data->vrrp_nf_table_name) have_firewall = true; #endif if (!have_firewall) { #ifdef _WITH_NFTABLES_ global_data->vrrp_nf_table_name = STRDUP(DEFAULT_NFTABLES_TABLE); #else log_message(LOG_INFO, "Adding iptables rules to default chains, but chains not configured"); global_data->vrrp_iptables_inchain = STRDUP(DEFAULT_IPTABLES_CHAIN_IN); global_data->vrrp_iptables_outchain = STRDUP(DEFAULT_IPTABLES_CHAIN_OUT); #ifdef _HAVE_LIBIPSET_ if (!global_data->using_ipsets) { global_data->using_ipsets = true; set_default_ipsets(); } #endif #endif } } #endif /* Add each VIP/eVIP's interface to the interface list, unless we aren't tracking it. * If the interface goes down, then we will not be able to re-add the address, and so * we should go to fault state. * If the vip hasn't specified an interface, default to the vrrp instance's i/f * or if it hasn't got one, the global default_interface. If we still haven't got * an interface, remove the address. */ for (vip_list = &vrrp->vip; vip_list; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL) { list_for_each_entry_safe(ip_addr, ip_addr_tmp, vip_list, e_list) { #ifdef _HAVE_VRRP_VMAC_ /* Check sanity regarding use_vmac. * use_vmac applies if (no interface specified or interface == vrrp_interface) AND address families match * use_vmac_addr applies otherwise. */ if (ip_addr->use_vmac) { ip_addr->use_vmac = false; /* It will be set true if needed */ if ((!ip_addr->ifp || ip_addr->ifp == vrrp->configured_ifp) && __test_bit(VRRP_VMAC_BIT, &vrrp->flags) && ip_addr->ifa.ifa_family == vrrp->family) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) use_vmac specified for VIP/eVIP %s and vrrp instance", vrrp->iname, ipaddresstos(NULL, ip_addr)); ip_addr->ifp = vrrp->ifp; } else if (((ip_addr->ifp && ip_addr->ifp != vrrp->configured_ifp) || ip_addr->ifa.ifa_family != vrrp->family) && __test_bit(VRRP_VMAC_ADDR_BIT, &vrrp->flags)) report_config_error(CONFIG_GENERAL_ERROR, "(%s) use_vmac_addr specified and use_vmac specified for VIP/eVIP %s", vrrp->iname, ipaddresstos(NULL, ip_addr)); else ip_addr->use_vmac = true; } if (!ip_addr->ifp || ip_addr->ifp == vrrp->configured_ifp) { if_sorted = true; if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && vrrp->family == ip_addr->ifa.ifa_family) ip_addr->ifp = vrrp->ifp; else { ip_addr->ifp = vrrp->configured_ifp; if (ip_addr->use_vmac || (__test_bit(VRRP_VMAC_ADDR_BIT, &vrrp->flags) && ip_addr->ifa.ifa_family != vrrp->family)) if_sorted = false; } } else if_sorted = !(ip_addr->use_vmac || __test_bit(VRRP_VMAC_ADDR_BIT, &vrrp->flags)); if (!if_sorted) { /* Now add VMACs for any addresses */ base_ifp = ip_addr->ifp; ifp = if_get_by_vmac(vrrp->vrid, ip_addr->ifa.ifa_family, ip_addr->ifp, __test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags) ? vrrp->ll_addr : NULL); if (!ifp) { ifp = create_vmac_name(global_data->vmac_addr_prefix ? global_data->vmac_addr_prefix : global_data->vmac_prefix ? global_data->vmac_prefix : "vrrp", vrrp->vrid, ip_addr->ifa.ifa_family); if (!__test_bit(CONFIG_TEST_BIT, &debug)) { /* For now create a dummy vrrp_instance to add the VMAC i/f */ vrrp_t addr_vrrp = { .vrid = vrrp->vrid }; addr_vrrp.ifp = ifp; addr_vrrp.family = ip_addr->ifa.ifa_family; strcpy(addr_vrrp.vmac_ifname, ifp->ifname); addr_vrrp.iname = vrrp->iname; addr_vrrp.configured_ifp = base_ifp; addr_vrrp.saddr.ss_family = AF_UNSPEC; if (__test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags)) { __set_bit(VRRP_VMAC_MAC_SPECIFIED, &addr_vrrp.flags); memcpy(addr_vrrp.ll_addr, vrrp->ll_addr, sizeof(vrrp->ll_addr)); } netlink_link_add_vmac(&addr_vrrp, NULL); } else { ifp->is_ours = true; ifp->if_type = IF_TYPE_MACVLAN; ifp->base_ifp = ip_addr->ifp; if (__test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags)) memcpy(ifp->hw_addr, vrrp->ll_addr, sizeof(vrrp->ll_addr)); else { ifp->hw_addr[0] = ll_addr[0]; ifp->hw_addr[1] = ll_addr[1]; ifp->hw_addr[2] = ll_addr[2]; ifp->hw_addr[3] = ll_addr[3]; ifp->hw_addr[4] = ip_addr->ifa.ifa_family == AF_INET ? 0x01 : 0x02; ifp->hw_addr[5] = vrrp->vrid; } } if (!ip_addr->dont_track) add_vrrp_to_interface(vrrp, ifp, 0, false, false, TRACK_ADDR); } ip_addr->ifp = ifp; } #else ip_addr->ifp = vrrp->ifp; #endif if (!ip_addr->ifp) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): no interface for %svip %s - removing", vrrp->iname, vip_list == &vrrp->vip ? "" : "e", ipaddresstos(NULL, ip_addr)); free_ipaddress(ip_addr); continue; } #ifdef _HAVE_VRRP_VMAC_ if (ip_addr->ifp != vrrp->ifp) { if (ip_addr->ifp->is_ours) use_extra_vmac = true; else use_extra_if = true; } #endif /* If the vrrp instance doesn't track its primary interface, * ensure that VIPs/eVIPs don't cause it to be tracked. */ if (!ip_addr->dont_track && (!__test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags) || (ip_addr->ifp != vrrp->ifp #ifdef _HAVE_VRRP_VMAC_ && ip_addr->ifp != IF_BASE_IFP(vrrp->ifp) #endif ))) add_vrrp_to_interface(vrrp, ip_addr->ifp, 0, false, false, TRACK_ADDR); if (ip_addr->ifa.ifa_family == AF_INET) have_ipv4_instance = true; else have_ipv6_instance = true; } } #ifdef _HAVE_VRRP_VMAC_ if (vrrp->vmac_garp_intvl.tv_sec == TIME_T_PARAMETER_UNSET) { vrrp->vmac_garp_intvl.tv_sec = global_data->vrrp_vmac_garp_intvl; if (global_data->vrrp_vmac_garp_all_if) __set_bit(VRRP_FLAG_VMAC_GARP_ALL_IF, &vrrp->flags); } /* If there are no extra interfaces, disable vmac_garp_intvl */ if (vrrp->vmac_garp_intvl.tv_sec) { if ((!use_extra_if && !use_extra_vmac) || (!use_extra_vmac && !__test_bit(VRRP_FLAG_VMAC_GARP_ALL_IF, &vrrp->flags))) vrrp->vmac_garp_intvl.tv_sec = 0; } #endif if (__test_bit(VRRP_FLAG_ALLOW_NO_VIPS, &vrrp->flags) && vrrp->strict_mode) { report_config_error(CONFIG_WARNING, "(%s) no_virtual_ipaddress and strict mode incompatible, clearing no_virtual_ipaddress", vrrp->iname); __clear_bit(VRRP_FLAG_ALLOW_NO_VIPS, &vrrp->flags); } if (!__test_bit(VRRP_FLAG_ALLOW_NO_VIPS, &vrrp->flags) && list_empty(&vrrp->vip)) { if (vrrp->version == VRRP_VERSION_3 || vrrp->strict_mode) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) No VIP specified; at least one is required", vrrp->iname); return false; } report_config_error(CONFIG_WARNING, "(%s) No VIP specified; at least one is sensible", vrrp->iname); } /* In case of VRRP SYNC, we have to carefully check that we are * not running floating priorities on any VRRP instance, unless * sgroup_tracking_weight is set. * If address owner, then we must totally ignore weights. */ if ((vrrp->sync && !vrrp->sync->sgroup_tracking_weight) || vrrp->base_priority == VRRP_PRIO_OWNER) { bool sync_no_tracking_weight = (vrrp->sync && !vrrp->sync->sgroup_tracking_weight); /* Set weight to 0 of any interface we are tracking, * unless we are the address owner, in which case stop tracking it */ list_for_each_entry_safe(tip, tip_tmp, &vrrp->track_ifp, e_list) { if (tip->weight && tip->weight != VRRP_NOT_TRACK_IF) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) ignoring %s" " tracked interface %sdue to %s" , vrrp->iname , tip->ifp->ifname , sync_no_tracking_weight ? "weight " : "" , sync_no_tracking_weight ? "SYNC group" : "address owner"); if (sync_no_tracking_weight) tip->weight = 0; else free_track_if(tip); } } /* Ignore any weighted script */ list_for_each_entry_safe(sc, sc_tmp, &vrrp->track_script, e_list) { if (sc->weight) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) ignoring " "tracked script %s with weights due to %s" , vrrp->iname , sc->scr->sname , sync_no_tracking_weight ? "SYNC group" : "address_owner"); free_track_script(sc); } } /* Set tracking files to unweighted if weight not explicitly set, otherwise ignore */ list_for_each_entry_safe(tfl, tfl_tmp, &vrrp->track_file, e_list) { if (tfl->weight == 1) { /* weight == 1 is the default */ report_config_error(CONFIG_GENERAL_ERROR, "(%s) ignoring weight from " "tracked file %s due to %s." " specify weight 0" , vrrp->iname , tfl->file->fname , sync_no_tracking_weight ? "SYNC group" : "address_owner"); tfl->weight = 0; } else if (tfl->weight) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) ignoring " "tracked file %s with weight %d due to %s" , vrrp->iname , tfl->file->fname , tfl->weight , sync_no_tracking_weight ? "SYNC group" : "address_owner"); free_track_file_monitor(tfl); } } #ifdef _WITH_BFD_ /* Ignore any weighted tracked bfd */ list_for_each_entry_safe(tbfd, tbfd_tmp, &vrrp->track_bfd, e_list) { if (tbfd->weight) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) ignoring " "tracked bfd %s with weight %d due to %s" , vrrp->iname , tbfd->bfd->bname , tbfd->weight , sync_no_tracking_weight ? "SYNC group" : "address_owner"); free_track_bfd(tbfd); } } #endif } /* Add us to the vrrp list of the script, and update * effective_priority, flags_if_fault and num_track_fault */ list_for_each_entry_safe(sc, sc_tmp, &vrrp->track_script, e_list) { vrrp_script_t *vsc = sc->scr; if (vrrp->base_priority == VRRP_PRIO_OWNER && sc->weight) { /* Is this duplicating the code with comment "Ignore any weighted script"? */ report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot have weighted track script" " '%s' with priority %d" , vrrp->iname , vsc->sname , VRRP_PRIO_OWNER); free_track_script(sc); continue; } add_vrrp_to_track_script(vrrp, sc); } /* Add our track files to the tracking file tracking_obj list */ list_for_each_entry(tfl, &vrrp->track_file, e_list) add_obj_to_track_file(vrrp, tfl, vrrp->iname, dump_tracking_vrrp); #ifdef _WITH_TRACK_PROCESS_ /* Add our track processes to the tracking process tracking_vrrp list */ list_for_each_entry(tpr, &vrrp->track_process, e_list) add_vrrp_to_track_process(vrrp, tpr); #endif #ifdef _WITH_BFD_ /* Add our track bfd to the tracking bfd tracking_vrrp list */ list_for_each_entry(tbfd, &vrrp->track_bfd, e_list) add_vrrp_to_track_bfd(vrrp, tbfd); #endif if (!vrrp->ifp || vrrp->ifp->ifindex) { if (!reload && interface_already_existed) { vrrp->vipset = true; /* Set to force address removal */ } /* See if we need to set promote_secondaries */ if (vrrp->ifp && __test_bit(VRRP_FLAG_PROMOTE_SECONDARIES, &vrrp->flags) && !vrrp->ifp->promote_secondaries && !__test_bit(CONFIG_TEST_BIT, &debug)) set_promote_secondaries(vrrp->ifp); } /* Check if there are any route/rules we need to monitor */ list_for_each_entry(route, &vrrp->vroutes, e_list) { if (!route->dont_track) { if (route->family == AF_INET) monitor_ipv4_routes = true; else monitor_ipv6_routes = true; /* If the route specifies an interface, this vrrp instance should track the interface */ if (route->oif) add_vrrp_to_interface(vrrp, route->oif, 0, false, false, TRACK_ROUTE); } } list_for_each_entry(rule, &vrrp->vrules, e_list) { if (!rule->dont_track) { if (rule->family == AF_INET) monitor_ipv4_rules = true; else monitor_ipv6_rules = true; /* If the rule specifies an interface, this vrrp instance should track the interface */ if (rule->iif) add_vrrp_to_interface(vrrp, rule->iif, 0, false, false, TRACK_RULE); if (rule->oif) add_vrrp_to_interface(vrrp, rule->oif, 0, false, false, TRACK_RULE); } } /* alloc send buffer */ vrrp_alloc_send_buffer(vrrp); vrrp_build_pkt(vrrp); return true; } static void sync_group_tracking_init(void) { vrrp_sgroup_t *sgroup; tracked_sc_t *sc; vrrp_script_t *vsc; tracked_if_t *tif; tracked_file_monitor_t *tfl; #ifdef _WITH_TRACK_PROCESS_ tracked_process_t *tpr; #endif #ifdef _WITH_BFD_ tracked_bfd_t *tbfd; #endif vrrp_t *vrrp; bool sgroup_has_prio_owner; /* Add sync group members to the vrrp list of the script, file, i/f, * and update effective_priority, flags_if_fault and num_track_fault */ list_for_each_entry(sgroup, &vrrp_data->vrrp_sync_group, e_list) { if (list_empty(&sgroup->vrrp_instances)) continue; /* Find out if any of the sync group members are address owners, since then * we cannot have weights */ sgroup_has_prio_owner = false; list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) { if (vrrp->base_priority == VRRP_PRIO_OWNER) { sgroup_has_prio_owner = true; break; } } list_for_each_entry(sc, &sgroup->track_script, e_list) { vsc = sc->scr; if (sgroup_has_prio_owner && sc->weight) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot have weighted track" " script '%s' with member having" " priority %d - clearing weight" , sgroup->gname , vsc->sname , VRRP_PRIO_OWNER); sc->weight = 0; } list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) add_vrrp_to_track_script(vrrp, sc); } /* tracked files */ list_for_each_entry(tfl, &sgroup->track_file, e_list) { if (sgroup_has_prio_owner && tfl->weight) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot have weighted track" " file '%s' with member having" " priority %d - setting weight 0" , sgroup->gname , tfl->file->fname , VRRP_PRIO_OWNER); tfl->weight = 0; } list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) add_obj_to_track_file(vrrp, tfl, vrrp->iname, dump_tracking_vrrp); } #ifdef _WITH_TRACK_PROCESS_ /* tracked processes */ list_for_each_entry(tpr, &sgroup->track_process, e_list) { if (sgroup_has_prio_owner && tpr->weight) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot have weighted track" " process '%s' with member having" " priority %d - setting weight 0" , sgroup->gname , tpr->process->pname , VRRP_PRIO_OWNER); tpr->weight = 0; } list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) add_vrrp_to_track_process(vrrp, tpr); } #endif #ifdef _WITH_BFD_ /* tracked bfd */ list_for_each_entry(tbfd, &sgroup->track_bfd, e_list) { if (sgroup_has_prio_owner && tbfd->weight) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot have weighted track" " bfd '%s' with member having" " priority %d - setting weight 0" , sgroup->gname , tbfd->bfd->bname , VRRP_PRIO_OWNER); tbfd->weight = 0; } list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) add_vrrp_to_track_bfd(vrrp, tbfd); } #endif /* tracked interfaces */ list_for_each_entry(tif, &sgroup->track_ifp, e_list) { if (sgroup_has_prio_owner && tif->weight) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot have weighted track" " interface '%s' with member having" " priority %d - clearing weight" , sgroup->gname , tif->ifp->ifname , VRRP_PRIO_OWNER); tif->weight = 0; } list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) add_vrrp_to_interface(vrrp, tif->ifp, tif->weight, tif->weight_reverse, true, TRACK_SG); } /* Set default smtp_alert */ if (sgroup->smtp_alert == -1) { if (global_data->smtp_alert_vrrp != -1) sgroup->smtp_alert = global_data->smtp_alert_vrrp; else if (global_data->smtp_alert != -1) sgroup->smtp_alert = global_data->smtp_alert; else sgroup->smtp_alert = false; } } } static void process_static_entries(void) { ip_route_t *route; ip_rule_t *rule; list_for_each_entry(route, &vrrp_data->static_routes, e_list) { if (!route->track_group) continue; if (route->family == AF_INET) monitor_ipv4_routes = true; else monitor_ipv6_routes = true; } list_for_each_entry(rule, &vrrp_data->static_rules, e_list) { if (!rule->track_group) continue; if (rule->family == AF_INET) monitor_ipv4_rules = true; else monitor_ipv6_rules = true; } } static void remove_residual_vips(void) { vrrp_t *vrrp; ip_address_t *ip_addr; list_head_t *ifq; list_head_t *vip_list; interface_t *ifp; sin_addr_t *saddr; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->vipset) { /* Remove any addresses configured on interfaces if they match any * VIP/eVIP addresses since we must not use them as source addresses * of adverts. They could exist if keepalived crashed the last time * it ran and it wasn't able to clean up. */ vip_list = &vrrp->vip; do { list_for_each_entry(ip_addr, vip_list, e_list) { /* Check primary address for family, then check list */ if (ip_addr->ifa.ifa_family == AF_INET) { if (inaddr_equal(AF_INET, &ip_addr->ifp->sin_addr, &ip_addr->u.sin.sin_addr)) { ip_addr->ifp->sin_addr.s_addr = 0; continue; } list_for_each_entry(saddr, &ip_addr->ifp->sin_addr_l, e_list) { if (inaddr_equal(AF_INET, &ip_addr->u.sin.sin_addr, &saddr->u.sin_addr)) { if_extra_ipaddress_free(saddr); break; } } } else { if (!IN6_IS_ADDR_LINKLOCAL(&ip_addr->u.sin6_addr)) continue; if (inaddr_equal(AF_INET6, &ip_addr->ifp->sin6_addr, &ip_addr->u.sin6_addr)) { CLEAR_IP6_ADDR(&ip_addr->ifp->sin6_addr); continue; } list_for_each_entry(saddr, &ip_addr->ifp->sin6_addr_l, e_list) { if (inaddr_equal(AF_INET6, &ip_addr->u.sin6_addr, &saddr->u.sin6_addr)) { if_extra_ipaddress_free(saddr); break; } } } } vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL; } while (vip_list); } } /* Promote address from list to i/f if none on i/f */ ifq = get_interface_queue(); list_for_each_entry(ifp, ifq, e_list) { if (ifp->sin_addr.s_addr == 0 && !list_empty(&ifp->sin_addr_l)) { saddr = list_first_entry(&ifp->sin_addr_l, sin_addr_t, e_list); ifp->sin_addr = saddr->u.sin_addr; if_extra_ipaddress_free(saddr); } if (IN6_IS_ADDR_UNSPECIFIED(&ifp->sin6_addr) && !list_empty(&ifp->sin6_addr_l)) { saddr = list_first_entry(&ifp->sin6_addr_l, sin_addr_t, e_list); ifp->sin6_addr = saddr->u.sin6_addr; if_extra_ipaddress_free(saddr); } } } static void set_vrrp_src_addr(void) { vrrp_t *vrrp; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags) || !vrrp->ifp) continue; /* Make sure we have an IP address as needed */ if (VRRP_CONFIGURED_IFP(vrrp)->ifindex /* && vrrp->saddr.ss_family == AF_UNSPEC */) { /* Check the physical interface has a suitable address we can use. * We don't need an IPv6 address on the underlying interface if it is * a VMAC since we can create our own. */ bool addr_missing = false; if (vrrp->family == AF_INET) { if (!(VRRP_CONFIGURED_IFP(vrrp))->sin_addr.s_addr) addr_missing = true; } else { #ifdef _HAVE_VRRP_VMAC_ if (!__test_bit(VRRP_VMAC_BIT, &vrrp->flags)) #endif if (IN6_IS_ADDR_UNSPECIFIED(&VRRP_CONFIGURED_IFP(vrrp)->sin6_addr)) addr_missing = true; } if (addr_missing) { if (vrrp->saddr.ss_family != AF_UNSPEC) { if (!global_data->dynamic_interfaces) report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot find an IP address to use for interface %s", vrrp->iname, VRRP_CONFIGURED_IFP(vrrp)->ifname); vrrp->saddr.ss_family = AF_UNSPEC; } } else if (vrrp->family == AF_INET) inet_ip4tosockaddr(&VRRP_CONFIGURED_IFP(vrrp)->sin_addr, &vrrp->saddr); else if (vrrp->family == AF_INET6) { #ifdef _HAVE_VRRP_VMAC_ if (!__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) && ( #ifdef _HAVE_VRRP_IPVLAN_ __test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) || #endif __test_bit(VRRP_VMAC_BIT, &vrrp->flags))) { if (!IN6_IS_ADDR_UNSPECIFIED(&vrrp->ifp->sin6_addr)) inet_ip6tosockaddr(&vrrp->ifp->sin6_addr, &vrrp->saddr); } else #endif if (!IN6_IS_ADDR_UNSPECIFIED(&VRRP_CONFIGURED_IFP(vrrp)->sin6_addr)) inet_ip6tosockaddr(&VRRP_CONFIGURED_IFP(vrrp)->sin6_addr, &vrrp->saddr); } } } } static bool check_vrid_conflicts(void) { vrrp_t *vrrp; vrrp_t *vrrp1; void *vrrp_saddr, *vrrp1_saddr; bool had_error = false; sockaddr_t *mcast, *mcast1; unicast_peer_t *peer, *peer1; /* NOTE: The following isn't perfect, since macvlan interfaces may be deleted and * recreated on a different interface. However, it is checking the current situation. */ /* Make sure don't have same vrid on same interface with the same address family and same multicast address if multicast */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { /* Check none of the rest of the entries conflict */ if (list_is_last(&vrrp->e_list, &vrrp_data->vrrp)) break; vrrp1 = list_entry(vrrp->e_list.next, vrrp_t, e_list); list_for_each_entry_from(vrrp1, &vrrp_data->vrrp, e_list) { /* Address family or VRID don't match? */ if (vrrp->family != vrrp1->family || vrrp->vrid != vrrp1->vrid) continue; /* Unicast and multicast are separate VRID spaces */ if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) != __test_bit(VRRP_FLAG_UNICAST, &vrrp1->flags)) continue; if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { /* They are both unicasting */ /* If they are configured on different interfaces, no match */ if (vrrp->ifp && vrrp1->ifp && vrrp->ifp != vrrp1->ifp) continue; /* Check if the local addresses match */ if (vrrp->family == AF_INET) { /* Check if both vrrp and vrrp1 have known addresses at the moment */ if ((!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags) && !(vrrp->ifp && vrrp->ifp->sin_addr.s_addr)) || (!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp1->flags) && !(vrrp1->ifp && vrrp1->ifp->sin_addr.s_addr))) continue; vrrp_saddr = vrrp->saddr.ss_family == AF_INET ? &PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr : &vrrp->ifp->sin_addr; vrrp1_saddr = vrrp1->saddr.ss_family == AF_INET ? &PTR_CAST(struct sockaddr_in, &vrrp1->saddr)->sin_addr : &vrrp1->ifp->sin_addr; } else { /* Check if both vrrp and vrrp1 have known addresses at the moment */ if ((!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags) && !(vrrp->ifp && !IN6_IS_ADDR_UNSPECIFIED(&vrrp->ifp->sin6_addr))) || (!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp1->flags) && !(vrrp1->ifp && !IN6_IS_ADDR_UNSPECIFIED(&vrrp1->ifp->sin6_addr)))) continue; vrrp_saddr = vrrp->saddr.ss_family == AF_INET6 ? &PTR_CAST(struct sockaddr_in6, &vrrp->saddr)->sin6_addr : &vrrp->ifp->sin6_addr; vrrp1_saddr = vrrp1->saddr.ss_family == AF_INET6 ? &PTR_CAST(struct sockaddr_in6, &vrrp1->saddr)->sin6_addr : &vrrp1->ifp->sin6_addr; } if (vrrp_saddr && vrrp1_saddr && inet_inaddrcmp(vrrp->family, vrrp_saddr, vrrp1_saddr)) continue; /* Don't allow duplicate VRIDs with interface specified for one but not the other */ /* Mark the VRRP instances as have duplicate instances with same VRID/local address/interface */ __set_bit(VRRP_FLAG_UNICAST_DUPLICATE_VRID, &vrrp->flags); __set_bit(VRRP_FLAG_UNICAST_DUPLICATE_VRID, &vrrp1->flags); bool unicast_peer_matched = false; list_for_each_entry(peer, &vrrp->unicast_peer, e_list) { list_for_each_entry(peer1, &vrrp1->unicast_peer, e_list) { if (inet_sockaddrcmp(&peer->address, &peer1->address) == 0) unicast_peer_matched = true; } } if (!unicast_peer_matched) continue; report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate VRID conflict with %s VRID %d", vrrp->iname, vrrp1->iname, vrrp->vrid); had_error = true; continue; } /* The vrrp instances are using multicasting */ if ((IF_BASE_IFP(VRRP_CONFIGURED_IFP(vrrp)) != IF_BASE_IFP(VRRP_CONFIGURED_IFP(vrrp1)) #if defined _HAVE_VRRP_VMAC_ && defined HAVE_IFLA_LINK_NETNSID && (vrrp->configured_ifp->base_netns_id == -1 || vrrp->configured_ifp->base_netns_id != vrrp1->configured_ifp->base_netns_id || vrrp->configured_ifp->base_ifindex != vrrp1->configured_ifp->base_ifindex) #endif )) continue; /* If multicast addresses are different, then no conflict */ if (vrrp->family == AF_INET) { mcast = vrrp->mcast_daddr.ss_family == AF_UNSPEC ? PTR_CAST(sockaddr_t, &global_data->vrrp_mcast_group4) : &vrrp->mcast_daddr; mcast1 = vrrp1->mcast_daddr.ss_family == AF_UNSPEC ? PTR_CAST(sockaddr_t, &global_data->vrrp_mcast_group4) : &vrrp1->mcast_daddr; } else { mcast = vrrp->mcast_daddr.ss_family == AF_UNSPEC ? PTR_CAST(sockaddr_t, &global_data->vrrp_mcast_group6) : &vrrp->mcast_daddr; mcast1 = vrrp1->mcast_daddr.ss_family == AF_UNSPEC ? PTR_CAST(sockaddr_t, &global_data->vrrp_mcast_group6) : &vrrp1->mcast_daddr; } if (memcmp(mcast, mcast1, vrrp->family == AF_INET ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6))) continue; #ifdef _HAVE_VRRP_VMAC_ if (global_data->allow_if_changes && (VRRP_CONFIGURED_IFP(vrrp)->changeable_type || VRRP_CONFIGURED_IFP(vrrp1)->changeable_type)) { if (VRRP_CONFIGURED_IFP(vrrp)->changeable_type) { vrrp->num_config_faults++; __set_bit(VRRP_FLAG_DUPLICATE_VRID_FAULT, &vrrp->flags); } else { vrrp1->num_config_faults++; __set_bit(VRRP_FLAG_DUPLICATE_VRID_FAULT, &vrrp1->flags); } log_message(LOG_INFO, "(%s) - warning, VRID %d for IPv%d" " is currently duplicated on %s" , vrrp->iname , vrrp->vrid , vrrp->family == AF_INET ? 4 : 6 , vrrp1->iname); } else #endif if (VRRP_CONFIGURED_IFP(vrrp)->ifindex) { report_config_error(CONFIG_GENERAL_ERROR, "%s and %s both use VRID %d" " with IPv%d on interface %s" , vrrp->iname , vrrp1->iname , vrrp->vrid , vrrp->family == AF_INET ? 4 : 6 , IF_BASE_IFP(VRRP_CONFIGURED_IFP(vrrp))->ifname); had_error = true; continue; } #ifdef _HAVE_VRRP_VMAC_ VRRP_CONFIGURED_IFP(vrrp)->seen_interface = true; IF_BASE_IFP(VRRP_CONFIGURED_IFP(vrrp))->seen_interface = true; #endif } } return had_error; } #ifdef _HAVE_VRRP_VMAC_ static void check_vmac_conflicts(void) { vrrp_t *vrrp, *vrrp1; ip_address_t *vip, *vip1; list_head_t *vip_list, *vip_list1; /* Now check that independant vrrp instances (i.e. not in a sync group) * are not trying to use the same VMAC (macvlan) interface. */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { list_for_each_entry(vrrp1, &vrrp_data->vrrp, e_list) { if (vrrp == vrrp1) break; /* If the VRIDs are different, there cannot be a conflict */ if (vrrp->vrid != vrrp1->vrid) continue; /* If they are in the same sync group, they can use the same VMAC */ if (vrrp->sync && vrrp->sync == vrrp1->sync) continue; if (vrrp->family != vrrp1->family && !__test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags) && !__test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp1->flags)) continue; /* Check vrrp's vmac against vrrp1 VIPs */ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && (vrrp->family == vrrp1->family || __test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp1->flags))) { /* Only check if vrrp families match, or evips if have evips from other family */ for (vip_list = vrrp->family == vrrp1->family ? &vrrp1->vip : &vrrp1->evip; vip_list; vip_list = vip_list == &vrrp1->vip ? &vrrp1->evip : NULL) { list_for_each_entry(vip, vip_list, e_list) { if (vrrp->ifp == vip->ifp) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VIP/eVIP %s uses same VMAC as VRRP instance %s, disabling %s", vrrp1->iname, ipaddresstos(NULL, vip), vrrp->iname, vrrp->iname); vrrp->num_config_faults++; } } } } /* Check vrrp's VIP's i/fs against vrrp1's VIP's i/fs */ for (vip_list = &vrrp->vip; vip_list; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL) { for (vip_list1 = &vrrp1->vip; vip_list1; vip_list1 = vip_list1 == &vrrp1->vip ? &vrrp1->evip : NULL) { if (vrrp->family != vrrp1->family) { if (vip_list == &vrrp->vip && vip_list1 == &vrrp1->vip) continue; if (vip_list == &vrrp->vip && vip_list1 == &vrrp1->evip && !__test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp1->flags)) continue; if (vip_list == &vrrp->evip && !__test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags) && vip_list1 == &vrrp1->vip) continue; if (vip_list == &vrrp->evip && !__test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags) && vip_list1 == &vrrp1->evip && !__test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp1->flags)) continue; } list_for_each_entry(vip, vip_list, e_list) { list_for_each_entry(vip1, vip_list1, e_list) { if (vip->ifp->is_ours && vip->ifp == vip1->ifp) { char vip1_str[IPADDRESSTOS_BUF_LEN]; ipaddresstos(vip1_str, vip1); report_config_error(CONFIG_GENERAL_ERROR, "(%s) VIP/eVIP %s uses same VMAC as VRRP instance %s VIP/eVIP %s, disabling %s", vrrp1->iname, ipaddresstos(NULL, vip), vrrp->iname, vip1_str, vrrp->iname); vrrp->num_config_faults++; } } } } } } } } #endif bool vrrp_complete_init(void) { /* * e - Element equal to a specific VRRP instance * eo- Element equal to a specific group within old global group list */ vrrp_t *vrrp, *old_vrrp; vrrp_sgroup_t *sgroup, *sgroup_tmp; size_t max_mtu_len = 0; bool have_master, have_backup; vrrp_script_t *scr, *scr_tmp; unsigned quickest_takeover; unsigned vrrp_timeout_min = UINT_MAX; /* Set defaults if not specified, depending on strict mode */ if (global_data->vrrp_garp_lower_prio_rep == PARAMETER_UNSET) global_data->vrrp_garp_lower_prio_rep = global_data->vrrp_garp_rep; if (global_data->vrrp_garp_lower_prio_delay == PARAMETER_UNSET) global_data->vrrp_garp_lower_prio_delay = global_data->vrrp_garp_delay; /* 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->vrrp_notify_fifo.script) add_script_param(global_data->vrrp_notify_fifo.script, global_data->vrrp_notify_fifo.name); #if defined _WITH_IPTABLES_ && defined _WITH_NFTABLES_ /* It doesn't make sense to use both iptables and nftables; prefer nftables */ if (global_data->vrrp_iptables_inchain && global_data->vrrp_nf_table_name) { log_message(LOG_INFO, "Both iptables and nftables have been specified - ignoring iptables"); FREE_CONST_PTR(global_data->vrrp_iptables_inchain); FREE_CONST_PTR(global_data->vrrp_iptables_outchain); #if defined _HAVE_LIBIPSET_ if (global_data->using_ipsets) disable_ipsets(); #endif } #endif #if defined _HAVE_LIBIPSET_ if (!global_data->vrrp_iptables_inchain && global_data->using_ipsets == true) { log_message(LOG_INFO, "vrrp_ipsets has been specified but not vrrp_iptables - vrrp_ipsets will be ignored"); disable_ipsets(); } if (global_data->using_ipsets == PARAMETER_UNSET) { if (global_data->vrrp_iptables_inchain) { set_default_ipsets(); global_data->using_ipsets = true; } else global_data->using_ipsets = false; } #endif /* NOTE: A reload which changes the iptables/nftables configuration will not * work properly. However, it is hard to check it. We could check here that everything * matches between old_global_data and global_data, because vrrp_complete_instance() can * update the need for using iptables/nftables, and also because it can update the * requirement if there is a VMAC, at this point we don't have a clear picture of what * the firewall situation is. * * So, if someone changes the configuration, it will be a bit confused, but it is unlikely * to happen. */ /* Mark any scripts as insecure */ check_vrrp_script_security(); #if !defined _ONE_PROCESS_DEBUG_ && defined _WITH_LVS_ /* Only one process must run the script to process the global fifo, * so let the checker process do so. */ if (running_checker()) free_notify_script(&global_data->notify_fifo.script); #endif /* Make sure minimal instance configuration as been done */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!chk_min_cfg(vrrp)) return false; } /* Build synchronization group index, and remove any * empty groups */ list_for_each_entry_safe(sgroup, sgroup_tmp, &vrrp_data->vrrp_sync_group, e_list) { if (!sgroup->iname) { report_config_error(CONFIG_GENERAL_ERROR, "Sync group %s has no virtual router(s)." " removing" , sgroup->gname); free_sync_group(sgroup); continue; } if (!vrrp_sync_set_group(sgroup)) free_sync_group(sgroup); } /* Complete VRRP instance initialization */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!vrrp_complete_instance(vrrp)) return false; if (vrrp->ifp && vrrp->ifp->mtu > max_mtu_len) max_mtu_len = vrrp->ifp->mtu; if (vrrp->highest_other_priority) { quickest_takeover = vrrp->adver_int * 2 + (vrrp->version == VRRP_VERSION_2 ? (256U - vrrp->highest_other_priority) * 1000000 / 256 : (256U - vrrp->highest_other_priority) * vrrp->adver_int / 256); if (quickest_takeover < vrrp_timeout_min) vrrp_timeout_min = quickest_takeover; } } if (vrrp_timeout_min != UINT_MAX) register_thread_timeout_handler(vrrp_thread_timeout_handler, vrrp_timeout_min); /* Make sure we don't have duplicate VRIDs */ if (check_vrid_conflicts()) return false; #ifdef _HAVE_VRRP_VMAC_ check_vmac_conflicts(); #endif /* If we add VMAC interfaces, we read netlink messages, which * may include link down/link up, and these will alter num_track_fault and flags_if_fault * but that is initialised in initialise_tracking_priorities() called below. * We therefore need to clear num_track_fault and flags_if_fault here. */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->num_config_faults) __set_bit(VRRP_FAULT_FL_CONFIG_ERROR, &vrrp->flags_if_fault); else { vrrp->num_track_fault = 0; vrrp->flags_if_fault = 0; } } /* Remove any VIPs from the list of default addresses for interfaces */ if (!reload) remove_residual_vips(); set_vrrp_src_addr(); /* Build static track groups and remove empty groups */ static_track_group_init(); /* Add pointers from sync group tracked scripts, file and interfaces * to members of the sync groups. * This must be called after vrrp_complete_instance() since this adds * (possibly weighted) tracking objects to vrrp instances, but any * weighted tracking objects configured directly against a vrrp instance * in a sync group must have the tracking objects removed unless * sgroup_tracking_weight is set */ sync_group_tracking_init(); /* All the checks that can be done without actually loading the config * have been done now */ if (__test_bit(CONFIG_TEST_BIT, &debug)) return true; /* Create a notify FIFO if needed, and open it */ notify_fifo_open(&global_data->notify_fifo, &global_data->vrrp_notify_fifo, vrrp_notify_fifo_script_exit, "vrrp_"); /* If we have a global garp_delay add it to any interfaces without a garp_delay */ if (global_data->vrrp_garp_interval || global_data->vrrp_gna_interval) set_default_garp_delay(); /* See if any static routes or rules need monitoring */ process_static_entries(); /* If we are tracking any routes/rules, ask netlink to monitor them */ set_extra_netlink_monitoring(monitor_ipv4_routes, monitor_ipv6_routes, monitor_ipv4_rules, monitor_ipv6_rules); #ifdef _WITH_LINKBEAT_ /* We need to know the state of interfaces for the next loop */ init_interface_linkbeat(); #endif /* Initialise any tracking files */ init_track_files(&vrrp_data->vrrp_track_files); #ifdef _WITH_TRACK_PROCESS_ /* Initialise any process tracking */ if (!list_empty(&vrrp_data->vrrp_track_processes)) { if (!global_data->network_namespace) open_track_processes(); if (reload) reload_track_processes(); else init_track_processes(&vrrp_data->vrrp_track_processes); } #endif /* Check for instance down or changed priority due to an interface, script, file or bfd */ initialise_tracking_priorities(); /* Make sure that if any sync group has member wanting to start in * master state, then all can start in master state. */ list_for_each_entry(sgroup, &vrrp_data->vrrp_sync_group, e_list) { have_backup = false; have_master = false; list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) { if (vrrp->wantstate == VRRP_STATE_BACK || vrrp->base_priority != VRRP_PRIO_OWNER) have_backup = true; if (vrrp->wantstate == VRRP_STATE_MAST) have_master = true; if (have_master && have_backup) { /* This looks wrong using the same loop variables as a containing * loop, but we break out of the outer loop after this loop */ list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) { if (vrrp->wantstate == VRRP_STATE_MAST) vrrp->wantstate = VRRP_STATE_BACK; } break; } } } // What we want to do is make all the settings for vrrp instances, including scripts in init // Then copy old vrrp master/backup in !fault or num_script_init // and then go through and set up sync groups in fault or init with counts // TODO-PQA /* Set all sync group members to fault state if sync group is in fault state */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->state == VRRP_STATE_FAULT || (vrrp->sync && vrrp->sync->state == VRRP_STATE_FAULT)) { vrrp->state = VRRP_STATE_FAULT; /* If we are reloading and the vrrp instance was already * in fault state, we don't need to notify again */ if (reload) { old_vrrp = vrrp_exist(vrrp, &old_vrrp_data->vrrp); if (old_vrrp && old_vrrp->state == VRRP_STATE_FAULT) continue; } log_message(LOG_INFO, "(%s) entering FAULT state", vrrp->iname); send_instance_notifies(vrrp); } } if (reload) { /* Now step through the old vrrp to set the status on matching new instances */ list_for_each_entry(old_vrrp, &old_vrrp_data->vrrp, e_list) { /* We work out for ourselves if the vrrp instance * should be in fault state, so it doesn't matter * if it was before */ if (old_vrrp->state == VRRP_STATE_FAULT) continue; vrrp = vrrp_exist(old_vrrp, &vrrp_data->vrrp); if (vrrp) { /* If we have detected a fault, don't override it */ if (vrrp->state == VRRP_STATE_FAULT || vrrp->num_script_init) continue; vrrp->state = old_vrrp->state; vrrp->wantstate = old_vrrp->state; } } /* Now see if any sync groups should be master */ list_for_each_entry(sgroup, &vrrp_data->vrrp_sync_group, e_list) { if (sgroup->num_member_fault || sgroup->num_member_init) continue; have_master = true; list_for_each_entry(vrrp, &sgroup->vrrp_instances, s_list) { if (vrrp->state != VRRP_STATE_MAST) { have_master = false; break; } } if (have_master) sgroup->state = VRRP_STATE_MAST; } } #ifdef _WITH_LVS_ /* Set up the lvs_syncd vrrp */ if (global_data->lvs_syncd.vrrp_name) { list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!strcmp(global_data->lvs_syncd.vrrp_name, vrrp->iname)) { global_data->lvs_syncd.vrrp = vrrp; break; } } if (!global_data->lvs_syncd.vrrp) { report_config_error(CONFIG_GENERAL_ERROR, "Unable to find vrrp instance %s" " for lvs_syncd - clearing lvs_syncd config" , global_data->lvs_syncd.vrrp_name); FREE_CONST_PTR(global_data->lvs_syncd.ifname); global_data->lvs_syncd.ifname = NULL; global_data->lvs_syncd.syncid = PARAMETER_UNSET; } else if (global_data->lvs_syncd.syncid == PARAMETER_UNSET) { /* If no syncid configured, use vrid */ global_data->lvs_syncd.syncid = global_data->lvs_syncd.vrrp->vrid; } /* vrrp_name is no longer used */ FREE_CONST_PTR(global_data->lvs_syncd.vrrp_name); global_data->lvs_syncd.vrrp_name = NULL; } else if (global_data->lvs_syncd.syncid == PARAMETER_UNSET) global_data->lvs_syncd.syncid = 0; #endif /* Identify and remove any unused tracking scripts */ list_for_each_entry_safe(scr, scr_tmp, &vrrp_data->vrrp_script, e_list) { if (list_empty(&scr->tracking_vrrp)) { report_config_error(CONFIG_GENERAL_ERROR, "Warning - script %s is not used", scr->sname); free_vscript(scr); } } alloc_vrrp_buffer(max_mtu_len ? max_mtu_len : DEFAULT_MTU); return true; } void vrrp_restore_interfaces_startup(void) { vrrp_t *vrrp; /* We don't know which VMACs are ours at startup. Delete all irrelevant addresses from VMACs here. But, * since if we configure a VMAC on a VMAC, it ends up on the underlying interface, we don't need to * have addresses for VMACs, accept the link local address based on the MAC of the underlying i/f. */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->vipset) vrrp_restore_interface(vrrp, false, true); } } /* Clear VIP|EVIP not present in the new data */ static void clear_diff_vrrp_vip(vrrp_t *old_vrrp, vrrp_t *vrrp) { list_head_t addr_list; bool fw_set = false; // !!!! TODO need to handle accept_mode changing - either remove all or add all. Do new entries get added for new VIPs? if (!old_vrrp->vipset) return; INIT_LIST_HEAD(&addr_list); get_diff_address(old_vrrp, vrrp, &addr_list); #ifdef _WITH_FIREWALL_ fw_set = (old_vrrp->base_priority != VRRP_PRIO_OWNER && !old_vrrp->accept); vrrp->firewall_rules_set = fw_set; /* If blocking traffic to VIPs is changing, update the firewall entries */ if ((vrrp->base_priority == VRRP_PRIO_OWNER || vrrp->accept) != (old_vrrp->base_priority == VRRP_PRIO_OWNER || old_vrrp->accept)) { if (vrrp->base_priority == VRRP_PRIO_OWNER || vrrp->accept) firewall_handle_accept_mode(old_vrrp, IPADDRESS_DEL, true); else firewall_handle_accept_mode(vrrp, IPADDRESS_ADD, true); } #endif clear_address_list(&addr_list, fw_set); free_ipaddress_list(&addr_list); } /* Clear virtual routes not present in the new data */ static void clear_diff_vrrp_vroutes(vrrp_t *old_vrrp, vrrp_t *vrrp) { clear_diff_routes(&old_vrrp->vroutes, &vrrp->vroutes); } /* Clear virtual rules not present in the new data */ static void clear_diff_vrrp_vrules(vrrp_t *old_vrrp, vrrp_t *vrrp) { clear_diff_rules(&old_vrrp->vrules, &vrrp->vrules); } /* Keep the state from before reload */ static bool restore_vrrp_state(vrrp_t *old_vrrp, vrrp_t *vrrp) { bool added_ip_addr = false; /* If the new state is master, we must be reloading from master */ vrrp->reload_master = vrrp->state == VRRP_STATE_MAST; /* Save old stats */ memcpy(vrrp->stats, old_vrrp->stats, sizeof(vrrp_stats)); #ifdef _WITH_VRRP_AUTH_ /* Keep ipsec AH seq_number */ memcpy(&vrrp->ipsecah_counter, &old_vrrp->ipsecah_counter, sizeof(seq_counter_t)); #endif /* Remember if we had vips up and add new ones if needed */ vrrp->vipset = old_vrrp->vipset; if (vrrp->vipset) { #ifdef _WITH_FIREWALL_ vrrp_handle_accept_mode(vrrp, IPADDRESS_ADD, false); #endif if (!list_empty(&vrrp->vip)) added_ip_addr = vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_VIP_TYPE, false); if (!list_empty(&vrrp->evip)) { if (vrrp_handle_ipaddress(vrrp, IPADDRESS_ADD, VRRP_EVIP_TYPE, false)) added_ip_addr = true; } if (!list_empty(&vrrp->vroutes)) { /* It is possible that some routes may have been deleted * by the kernel if, for example, they depended on a VIP * that has been removed, and in this case the kernel doesn't * notify us that the route has been deleted. We therefore * need to attempt to re-add all the virtual routes. */ vrrp_handle_iproutes(vrrp, IPROUTE_ADD, true); } if (!list_empty(&vrrp->vrules)) vrrp_handle_iprules(vrrp, IPRULE_ADD, false); } return added_ip_addr; } /* Diff when reloading configuration */ void clear_diff_vrrp(void) { vrrp_t *vrrp; vrrp_t *new_vrrp; bool have_new_addr; list_for_each_entry(vrrp, &old_vrrp_data->vrrp, e_list) { /* * Try to find this vrrp in the new conf data * reloaded. */ new_vrrp = vrrp_exist(vrrp, &vrrp_data->vrrp); if (!new_vrrp) { if (vrrp->state == VRRP_STATE_MAST) vrrp_restore_interface(vrrp, true, false); /* We used to send FAULT if an instance was deleted, so that * needs to continue as the default. If vrrp->notify_deleted * is set, we now send DELETED instead. */ if (vrrp->notify_deleted || vrrp->state != VRRP_STATE_FAULT) { vrrp->state = VRRP_STATE_DELETED; send_instance_notifies(vrrp); } #ifdef _HAVE_VRRP_VMAC_ /* Remove VMAC if one was created so long as no new VRRP instance is using it */ if (vrrp->ifp && vrrp->ifp->is_ours && list_empty(&vrrp->ifp->tracking_vrrp)) { netlink_link_del_vmac(vrrp); /* Need to delete ADDR VMACs */ } #endif #ifdef _WITH_DBUS_ /* Remove DBus object */ if (global_data->enable_dbus) dbus_remove_object(vrrp); #endif } } list_for_each_entry(vrrp, &old_vrrp_data->vrrp, e_list) { /* * Try to find this vrrp in the new conf data * reloaded. */ new_vrrp = vrrp_exist(vrrp, &vrrp_data->vrrp); if (!new_vrrp) continue; /* * If this vrrp instance exist in new * data, then perform a VIP|EVIP diff. */ // !!!! Isn't this only necessary if MASTER ???? TODO if (vrrp->state == VRRP_STATE_MAST) { /* virtual rules diff */ clear_diff_vrrp_vrules(vrrp, new_vrrp); /* virtual routes diff */ clear_diff_vrrp_vroutes(vrrp, new_vrrp); clear_diff_vrrp_vip(vrrp, new_vrrp); } #ifdef _HAVE_VRRP_VMAC_ /* * Remove VMAC/IPVLAN if it existed in old vrrp instance, * but not the new one. */ if (vrrp->ifp && vrrp->ifp->is_ours && ((__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && !__test_bit(VRRP_VMAC_BIT, &new_vrrp->flags)) #ifdef _HAVE_VRRP_IPVLAN_ || (__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) && !__test_bit(VRRP_IPVLAN_BIT, &new_vrrp->flags)) #endif )) { netlink_link_del_vmac(vrrp); } // What about VMACs for addresses? #endif /* reset the state */ have_new_addr = restore_vrrp_state(vrrp, new_vrrp); if (new_vrrp->state != VRRP_STATE_MAST) continue; if (have_new_addr || timerisset(&new_vrrp->garp_refresh)) { /* There were addresses added, or we do periodic GARP/GNA * refreshes, so send GARP/GNAs for them. * This is a bit over the top if only for added addresses, * since it will send GARPs/GNAs for * all the addresses, * but at least we will do so for the new addresses. */ vrrp_send_link_update(new_vrrp, new_vrrp->garp_rep); /* Add thread for second block of GARPs */ if (have_new_addr && new_vrrp->garp_delay) thread_add_timer(master, vrrp_gratuitous_arp_thread, new_vrrp, new_vrrp->garp_delay); } if (timerisset(&new_vrrp->garp_refresh)) thread_add_timer(master, vrrp_gratuitous_arp_refresh_thread, new_vrrp, (have_new_addr ? new_vrrp->garp_delay : 0) + timer_long(new_vrrp->garp_refresh)); #ifdef _HAVE_VRRP_VMAC_ if (timerisset(&new_vrrp->vmac_garp_intvl)) thread_add_timer(master, vrrp_gratuitous_arp_vmac_update_thread, new_vrrp, (have_new_addr ? new_vrrp->garp_delay : 0) + timer_long(new_vrrp->vmac_garp_intvl)); #endif } #ifdef _HAVE_VRRP_VMAC_ /* Remove any address VMACs that we had, but are no longer being used */ interface_t *ifp; bool found; list_head_t *vip_list; ip_address_t *vip; list_head_t *if_queue = get_interface_queue(); list_for_each_entry(ifp, if_queue, e_list) { if (!ifp->is_ours) continue; found = false; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->ifp == ifp) { found = true; break; } for (vip_list = &vrrp->vip; vip_list && !found; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL) { list_for_each_entry(vip, vip_list, e_list) { if (vip->ifp == ifp) { found = true; break; } } } } if (!found) { /* For now create a dummy vrrp_instance to delete the VMAC i/f */ vrrp_t addr_vrrp = { .ifp = ifp }; addr_vrrp.family = ifp->hw_addr[sizeof(ll_addr) - 2] == 0x01 ? AF_INET : AF_INET6; addr_vrrp.iname = vrrp->iname; strcpy(addr_vrrp.vmac_ifname, ifp->ifname); __set_bit(VRRP_VMAC_BIT, &addr_vrrp.flags); // This should be superfluous netlink_link_del_vmac(&addr_vrrp); } } #endif } /* Set script status to a sensible value on reload */ void clear_diff_script(void) { vrrp_script_t *vscript, *nvscript; bool different; int i; list_for_each_entry(vscript, &old_vrrp_data->vrrp_script, e_list) { nvscript = find_script_by_name(vscript->sname); if (nvscript) { /* Check if the scripts are the same */ if (vscript->script.num_args != nvscript->script.num_args || vscript->script.uid != nvscript->script.uid || vscript->script.gid != nvscript->script.gid || !vscript->script.path != !nvscript->script.path || (vscript->script.path && strcmp(vscript->script.path, nvscript->script.path))) continue; for (i = 0, different = false; i < vscript->script.num_args; i++) { if (strcmp(vscript->script.args[i], nvscript->script.args[i])) { different = true; break; } } if (different) continue; if (vscript->init_state == SCRIPT_INIT_STATE_INIT) { /* We need to undo the startup assumptions and apply new startup assumptions */ nvscript->init_state = SCRIPT_INIT_STATE_INIT; nvscript->result = nvscript->rise - 1; continue; } else if (vscript->init_state == SCRIPT_INIT_STATE_INIT_RELOAD) { nvscript->init_state = SCRIPT_INIT_STATE_INIT_RELOAD; continue; } /* Set the script result to match the previous result */ if (vscript->result < vscript->rise) { if (!vscript->result) nvscript->result = 0; else { nvscript->result = nvscript->rise - (vscript->rise - vscript->result); if (nvscript->result < 0) nvscript->result = 0; } log_message(LOG_INFO, "VRRP_Script(%s) considered unsuccessful on reload" , nvscript->sname); } else { if (vscript->result == vscript->rise + vscript->fall - 1) nvscript->result = nvscript->rise + nvscript->fall - 1; else { nvscript->result = nvscript->rise + (vscript->result - vscript->rise); if (nvscript->result >= nvscript->rise + nvscript->fall) nvscript->result = nvscript->rise + nvscript->fall - 1; } log_message(LOG_INFO, "VRRP_Script(%s) considered successful on reload" , nvscript->sname); } nvscript->last_status = vscript->last_status; nvscript->init_state = SCRIPT_INIT_STATE_DONE; } } } void set_previous_sync_group_states(void) { vrrp_sgroup_t *ogroup, *ngroup; list_for_each_entry(ngroup, &vrrp_data->vrrp_sync_group, e_list) { list_for_each_entry(ogroup, &old_vrrp_data->vrrp_sync_group, e_list) { if (!strcmp(ngroup->gname, ogroup->gname)) { if (ngroup->state == ogroup->state) ngroup->state_same_at_reload = true; break; } } } } #ifdef _WITH_BFD_ /* Set bfd status to match old instance */ void clear_diff_bfd(void) { vrrp_tracked_bfd_t *vbfd, *nvbfd; list_for_each_entry(vbfd, &old_vrrp_data->vrrp_track_bfds, e_list) { nvbfd = find_vrrp_tracked_bfd_by_name(vbfd->bname); if (nvbfd) nvbfd->bfd_up = vbfd->bfd_up; } } #endif #ifdef THREAD_DUMP void register_vrrp_fifo_addresses(void) { register_thread_address("vrrp_notify_fifo_script_exit", vrrp_notify_fifo_script_exit); register_thread_address("vrrp_rogue_timer_thread", vrrp_rogue_timer_thread); } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_iproute.c0000664000175000017500000017532514650656106015347 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: NETLINK IPv4 routes manipulation. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #if HAVE_DECL_RTA_ENCAP #include #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS #include #endif #if HAVE_DECL_LWTUNNEL_ENCAP_ILA #include #endif #endif #include #include #include /* local include */ #include "vrrp_iproute.h" #include "keepalived_netlink.h" #include "vrrp_data.h" #include "logger.h" #include "memory.h" #include "utils.h" #include "rttables.h" #include "vrrp_ip_rule_route_parser.h" #include "parser.h" /* Buffer sizes for netlink messages. Increase if needed. */ #define RTM_SIZE 1024 #define RTA_SIZE 1024 #define ENCAP_RTA_SIZE 128 /* Utility functions */ unsigned short add_addr2req(struct nlmsghdr *n, size_t maxlen, unsigned short type, ip_address_t *ip_address) { void *addr; size_t alen; if (!ip_address) return 0; if (IP_IS6(ip_address)) { addr = (void *) &ip_address->u.sin6_addr; alen = sizeof(ip_address->u.sin6_addr); } else { addr = (void *) &ip_address->u.sin.sin_addr; alen = sizeof(ip_address->u.sin.sin_addr); } return (unsigned short)addattr_l(n, maxlen, type, addr, alen); } #if HAVE_DECL_RTA_VIA static unsigned short add_addr_fam2req(struct nlmsghdr *n, size_t maxlen, unsigned short type, ip_address_t *ip_address) { void *addr; size_t alen; uint16_t family; if (!ip_address) return 0; if (IP_IS6(ip_address)) { addr = (void *)&ip_address->u.sin6_addr; alen = sizeof(ip_address->u.sin6_addr); } else { addr = (void *)&ip_address->u.sin.sin_addr; alen = sizeof(ip_address->u.sin.sin_addr); } family = ip_address->ifa.ifa_family; return (unsigned short)addattr_l2(n, maxlen, type, &family, sizeof(family), addr, alen); } #endif static unsigned short add_addr2rta(struct rtattr *rta, size_t maxlen, unsigned short type, ip_address_t *ip_address) { void *addr; size_t alen; if (!ip_address) return 0; if (IP_IS6(ip_address)) { addr = (void *)&ip_address->u.sin6_addr; alen = sizeof(ip_address->u.sin6_addr); } else { addr = (void *)&ip_address->u.sin.sin_addr; alen = sizeof(ip_address->u.sin.sin_addr); } return (unsigned short)rta_addattr_l(rta, maxlen, type, addr, alen); } #if HAVE_DECL_RTA_VIA static unsigned short add_addrfam2rta(struct rtattr *rta, size_t maxlen, unsigned short type, ip_address_t *ip_address) { void *addr; size_t alen; uint16_t family; if (!ip_address) return 0; if (IP_IS6(ip_address)) { addr = (void *)&ip_address->u.sin6_addr; alen = sizeof(ip_address->u.sin6_addr); } else { addr = (void *)&ip_address->u.sin.sin_addr; alen = sizeof(ip_address->u.sin.sin_addr); } family = ip_address->ifa.ifa_family; return (unsigned short)rta_addattr_l2(rta, maxlen, type, &family, sizeof(family), addr, alen); } #endif #if HAVE_DECL_RTA_ENCAP #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS static void add_encap_mpls(struct rtattr *rta, size_t len, const encap_t *encap) { rta_addattr_l(rta, len, MPLS_IPTUNNEL_DST, &encap->mpls.addr, encap->mpls.num_labels * sizeof(encap->mpls.addr[0])); } #endif static void add_encap_ip(struct rtattr *rta, size_t len, const encap_t *encap) { if (encap->flags & IPROUTE_BIT_ENCAP_ID) rta_addattr64(rta, len, LWTUNNEL_IP_ID, htobe64(encap->ip.id)); if (encap->ip.dst) rta_addattr_l(rta, len, LWTUNNEL_IP_DST, &encap->ip.dst->u.sin.sin_addr.s_addr, sizeof(encap->ip.dst->u.sin.sin_addr.s_addr)); if (encap->ip.src) rta_addattr_l(rta, len, LWTUNNEL_IP_SRC, &encap->ip.src->u.sin.sin_addr.s_addr, sizeof(encap->ip.src->u.sin.sin_addr.s_addr)); if (encap->flags & IPROUTE_BIT_ENCAP_DSFIELD) rta_addattr8(rta, len, LWTUNNEL_IP_TOS, encap->ip.tos); if (encap->flags & IPROUTE_BIT_ENCAP_HOPLIMIT) rta_addattr8(rta, len, LWTUNNEL_IP_TTL, encap->ip.ttl); if (encap->flags & IPROUTE_BIT_ENCAP_FLAGS) rta_addattr16(rta, len, LWTUNNEL_IP_FLAGS, encap->ip.flags); } #if HAVE_DECL_LWTUNNEL_ENCAP_ILA static void add_encap_ila(struct rtattr *rta, size_t len, const encap_t *encap) { rta_addattr64(rta, len, ILA_ATTR_LOCATOR, encap->ila.locator); } #endif static void add_encap_ip6(struct rtattr *rta, size_t len, const encap_t *encap) { if (encap->flags & IPROUTE_BIT_ENCAP_ID) rta_addattr64(rta, len, LWTUNNEL_IP6_ID, htobe64(encap->ip6.id)); if (encap->ip6.dst) rta_addattr_l(rta, len, LWTUNNEL_IP6_DST, &encap->ip6.dst->u.sin6_addr, sizeof(encap->ip6.dst->u.sin6_addr)); if (encap->ip6.src) rta_addattr_l(rta, len, LWTUNNEL_IP6_SRC, &encap->ip6.src->u.sin6_addr, sizeof(encap->ip6.src->u.sin6_addr)); if (encap->flags & IPROUTE_BIT_ENCAP_DSFIELD) rta_addattr8(rta, len, LWTUNNEL_IP6_TC, encap->ip6.tc); if (encap->flags & IPROUTE_BIT_ENCAP_HOPLIMIT) rta_addattr8(rta, len, LWTUNNEL_IP6_HOPLIMIT, encap->ip6.hoplimit); if (encap->flags & IPROUTE_BIT_ENCAP_FLAGS) rta_addattr16(rta, len, LWTUNNEL_IP6_FLAGS, encap->ip6.flags); } static bool add_encap(struct rtattr *rta, size_t len, encap_t *encap) { struct rtattr *nest; nest = rta_nest(rta, len, RTA_ENCAP); switch (encap->type) { #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS case LWTUNNEL_ENCAP_MPLS: add_encap_mpls(rta, len, encap); break; #endif case LWTUNNEL_ENCAP_IP: add_encap_ip(rta, len, encap); break; #if HAVE_DECL_LWTUNNEL_ENCAP_ILA case LWTUNNEL_ENCAP_ILA: add_encap_ila(rta, len, encap); break; #endif case LWTUNNEL_ENCAP_IP6: add_encap_ip6(rta, len, encap); break; default: log_message(LOG_INFO, "unknown encap type %d", encap->type); break; } rta_nest_end(rta, nest); rta_addattr16(rta, len, RTA_ENCAP_TYPE, encap->type); return true; } #endif static void add_nexthop(nexthop_t *nh, struct rtmsg *rtm, struct rtattr *rta, size_t len, struct rtnexthop *rtnh) { if (nh->addr) { if (rtm->rtm_family == nh->addr->ifa.ifa_family) rtnh->rtnh_len = (unsigned short)(rtnh->rtnh_len + add_addr2rta(rta, len, RTA_GATEWAY, nh->addr)); #if HAVE_DECL_RTA_VIA else rtnh->rtnh_len = (unsigned short)(rtnh->rtnh_len + add_addrfam2rta(rta, len, RTA_VIA, nh->addr)); #endif } if (nh->ifp) rtnh->rtnh_ifindex = (int)nh->ifp->ifindex; if (nh->mask & IPROUTE_BIT_WEIGHT) rtnh->rtnh_hops = nh->weight; rtnh->rtnh_flags = nh->flags; if (nh->realms) rtnh->rtnh_len = (unsigned short)(rtnh->rtnh_len + rta_addattr32(rta, len, RTA_FLOW, nh->realms)); #if HAVE_DECL_RTA_ENCAP if (nh->encap.type != LWTUNNEL_ENCAP_NONE) { unsigned short rta_len = rta->rta_len; add_encap(rta, rta_len, &nh->encap); rtnh->rtnh_len = (unsigned short)(rtnh->rtnh_len + rta->rta_len - rta_len); } #endif } static void add_nexthops(ip_route_t *route, struct nlmsghdr *nlh, struct rtmsg *rtm) { char buf[ENCAP_RTA_SIZE] __attribute__((aligned(__alignof__(struct rtattr)))); struct rtattr *rta = PTR_CAST(struct rtattr, buf); struct rtnexthop *rtnh; nexthop_t *nh; rta->rta_type = RTA_MULTIPATH; rta->rta_len = RTA_LENGTH(0); rtnh = RTA_DATA(rta); list_for_each_entry(nh, &route->nhs, e_list) { memset(rtnh, 0, sizeof(*rtnh)); rtnh->rtnh_len = sizeof(*rtnh); rta->rta_len = (unsigned short)(rta->rta_len + rtnh->rtnh_len); add_nexthop(nh, rtm, rta, sizeof(buf), rtnh); /* See -Wcast-align comment in keepalived_netlink.c, also applies to RTNH_NEXT */ rtnh = RTNH_NEXT(rtnh); } if (rta->rta_len > RTA_LENGTH(0)) addattr_l(nlh, sizeof(buf), RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); } /* Add/Delete IP route to/from a specific interface. * Note: By default we do not set the NLM_F_EXCL flag, and so the * equivalent ip route command to add a route is: ip route prepend ... */ static bool netlink_route(ip_route_t *iproute, int cmd) { struct { struct nlmsghdr n; struct rtmsg r; char buf[RTM_SIZE]; } req; char buf[RTA_SIZE] __attribute__((aligned(__alignof__(struct rtattr)))); struct rtattr *rta = PTR_CAST(struct rtattr, buf); memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); if (cmd == IPROUTE_DEL) { req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_DELROUTE; } else { req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE; if (cmd == IPROUTE_REPLACE) req.n.nlmsg_flags |= NLM_F_REPLACE; else if (iproute->mask & IPROUTE_BIT_ADD) req.n.nlmsg_flags |= NLM_F_EXCL; else if (iproute->mask & IPROUTE_BIT_APPEND) req.n.nlmsg_flags |= NLM_F_APPEND; req.n.nlmsg_type = RTM_NEWROUTE; } rta->rta_type = RTA_METRICS; rta->rta_len = RTA_LENGTH(0); req.r.rtm_family = iproute->family; if (iproute->table < 256) req.r.rtm_table = (unsigned char)iproute->table; else { req.r.rtm_table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), RTA_TABLE, iproute->table); } if (cmd == IPROUTE_DEL) { req.r.rtm_scope = RT_SCOPE_NOWHERE; if (iproute->mask & IPROUTE_BIT_TYPE) req.r.rtm_type = iproute->type; } else { req.r.rtm_scope = RT_SCOPE_UNIVERSE; req.r.rtm_type = iproute->type; } if (iproute->mask & IPROUTE_BIT_PROTOCOL) req.r.rtm_protocol = iproute->protocol; else req.r.rtm_protocol = RTPROT_KEEPALIVED; if (iproute->mask & IPROUTE_BIT_SCOPE) req.r.rtm_scope = iproute->scope; if (iproute->dst) { req.r.rtm_dst_len = iproute->dst->ifa.ifa_prefixlen; add_addr2req(&req.n, sizeof(req), RTA_DST, iproute->dst); } if (iproute->src) { req.r.rtm_src_len = iproute->src->ifa.ifa_prefixlen; add_addr2req(&req.n, sizeof(req), RTA_SRC, iproute->src); } if (iproute->pref_src) add_addr2req(&req.n, sizeof(req), RTA_PREFSRC, iproute->pref_src); //#if HAVE_DECL_RTA_NEWDST // if (iproute->as_to) // add_addr2req(&req.n, sizeof(req), RTA_NEWDST, iproute->as_to); //#endif if (iproute->via) { if (iproute->via->ifa.ifa_family == iproute->family) add_addr2req(&req.n, sizeof(req), RTA_GATEWAY, iproute->via); #if HAVE_DECL_RTA_VIA else add_addr_fam2req(&req.n, sizeof(req), RTA_VIA, iproute->via); #endif } #if HAVE_DECL_RTA_ENCAP if (iproute->encap.type != LWTUNNEL_ENCAP_NONE) { char encap_buf[ENCAP_RTA_SIZE] __attribute__((aligned(__alignof__(struct rtattr)))); struct rtattr *encap_rta = PTR_CAST(struct rtattr, encap_buf); encap_rta->rta_type = RTA_ENCAP; encap_rta->rta_len = RTA_LENGTH(0); add_encap(encap_rta, sizeof(encap_buf), &iproute->encap); if (encap_rta->rta_len > RTA_LENGTH(0)) addraw_l(&req.n, sizeof(encap_buf), RTA_DATA(encap_rta), RTA_PAYLOAD(encap_rta)); } #endif if (iproute->mask & IPROUTE_BIT_DSFIELD) req.r.rtm_tos = iproute->tos; if (iproute->oif) addattr32(&req.n, sizeof(req), RTA_OIF, iproute->oif->ifindex); if (iproute->mask & IPROUTE_BIT_METRIC) addattr32(&req.n, sizeof(req), RTA_PRIORITY, iproute->metric); req.r.rtm_flags = iproute->flags; if (iproute->realms) addattr32(&req.n, sizeof(req), RTA_FLOW, iproute->realms); #if HAVE_DECL_RTA_EXPIRES if (iproute->mask & IPROUTE_BIT_EXPIRES) addattr32(&req.n, sizeof(req), RTA_EXPIRES, iproute->expires); #endif #if HAVE_DECL_RTAX_CC_ALGO if (iproute->congctl) rta_addattr_l(rta, sizeof(buf), RTAX_CC_ALGO, iproute->congctl, strlen(iproute->congctl)); #endif if (iproute->mask & IPROUTE_BIT_RTT) rta_addattr32(rta, sizeof(buf), RTAX_RTT, iproute->rtt); if (iproute->mask & IPROUTE_BIT_RTTVAR) rta_addattr32(rta, sizeof(buf), RTAX_RTTVAR, iproute->rttvar); if (iproute->mask & IPROUTE_BIT_RTO_MIN) rta_addattr32(rta, sizeof(buf), RTAX_RTO_MIN, iproute->rto_min); if (iproute->features) rta_addattr32(rta, sizeof(buf), RTAX_FEATURES, iproute->features); if (iproute->mask & IPROUTE_BIT_MTU) rta_addattr32(rta, sizeof(buf), RTAX_MTU, iproute->mtu); if (iproute->mask & IPROUTE_BIT_WINDOW) rta_addattr32(rta, sizeof(buf), RTAX_WINDOW, iproute->window); if (iproute->mask & IPROUTE_BIT_SSTHRESH) rta_addattr32(rta, sizeof(buf), RTAX_SSTHRESH, iproute->ssthresh); if (iproute->mask & IPROUTE_BIT_CWND) rta_addattr32(rta, sizeof(buf), RTAX_CWND, iproute->cwnd); if (iproute->mask & IPROUTE_BIT_ADVMSS) rta_addattr32(rta, sizeof(buf), RTAX_ADVMSS, iproute->advmss); if (iproute->mask & IPROUTE_BIT_REORDERING) rta_addattr32(rta, sizeof(buf), RTAX_REORDERING, iproute->reordering); if (iproute->mask & IPROUTE_BIT_HOPLIMIT) rta_addattr32(rta, sizeof(buf), RTAX_HOPLIMIT, iproute->hoplimit); if (iproute->mask & IPROUTE_BIT_INITCWND) rta_addattr32(rta, sizeof(buf), RTAX_INITCWND, iproute->initcwnd); if (iproute->mask & IPROUTE_BIT_INITRWND) rta_addattr32(rta, sizeof(buf), RTAX_INITRWND, iproute->initrwnd); #if HAVE_DECL_RTAX_QUICKACK if (iproute->mask & IPROUTE_BIT_QUICKACK) rta_addattr32(rta, sizeof(buf), RTAX_QUICKACK, iproute->quickack); #endif #if HAVE_DECL_RTA_PREF if (iproute->mask & IPROUTE_BIT_PREF) addattr8(&req.n, sizeof(req), RTA_PREF, iproute->pref); #endif #if HAVE_DECL_RTAX_FASTOPEN_NO_COOKIE if (iproute->mask & IPROUTE_BIT_FASTOPEN_NO_COOKIE) rta_addattr32(rta, sizeof(buf), RTAX_FASTOPEN_NO_COOKIE, iproute->fastopen_no_cookie); #endif #if HAVE_DECL_RTA_TTL_PROPAGATE if (iproute->mask & IPROUTE_BIT_TTL_PROPAGATE) addattr8(&req.n, sizeof(req), RTA_TTL_PROPAGATE, iproute->ttl_propagate); #endif if (rta->rta_len > RTA_LENGTH(0)) { if (iproute->lock) rta_addattr32(rta, sizeof(buf), RTAX_LOCK, iproute->lock); addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(rta), RTA_PAYLOAD(rta)); } if (!list_empty(&iproute->nhs)) add_nexthops(iproute, &req.n, &req.r); #ifdef DEBUG_NETLINK_MSG size_t i, j; uint8_t *p; char lbuf[3072]; char *op = lbuf; log_message(LOG_INFO, "rtmsg buffer used %lu, rtattr buffer used %d", req.n.nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)), rta->rta_len); op += (size_t)snprintf(op, sizeof(lbuf) - (op - lbuf), "nlmsghdr %p(%u):", &req.n, req.n.nlmsg_len); for (i = 0, p = PTR_CAST(uint8_t, &req.n); i < sizeof(struct nlmsghdr); i++) op += (size_t)snprintf(op, sizeof(lbuf) - (op - lbuf), " %2.2hhx", *(p++)); log_message(LOG_INFO, "%s", lbuf); op = lbuf; op += (size_t)snprintf(op, sizeof(lbuf) - (op - lbuf), "rtmsg %p(%lu):", &req.r, req.n.nlmsg_len - sizeof(struct nlmsghdr)); for (i = 0, p = PTR_CAST(uint8_t, &req.r); i < req.n.nlmsg_len - sizeof(struct nlmsghdr); i++) op += (size_t)snprintf(op, sizeof(lbuf) - (op - lbuf), " %2.2hhx", *(p++)); for (j = 0; lbuf + j < op; j+= MAX_LOG_MSG) log_message(LOG_INFO, "%.*", MAX_LOG_MSG, lbuf+j); #endif /* This returns ESRCH if the address of via address doesn't exist */ /* ENETDOWN if dev p33p1.40 for example is down */ if (netlink_talk(&nl_cmd, &req.n) < 0) { #if HAVE_DECL_RTA_EXPIRES /* If an expiry was set on the route, it may have disappeared already */ if (cmd != IPROUTE_DEL || !(iproute->mask & IPROUTE_BIT_EXPIRES)) #endif return true; } return false; } /* Add/Delete a list of IP routes */ bool netlink_rtlist(list_head_t *rt_list, int cmd, bool force) { ip_route_t *ip_route; /* No routes to add */ if (list_empty(rt_list)) return false; list_for_each_entry(ip_route, rt_list, e_list) { if ((cmd == IPROUTE_DEL) == ip_route->set || force) { if (!netlink_route(ip_route, cmd)) { if (cmd == IPROUTE_DEL) ip_route->set = false; } else if (cmd != IPROUTE_ADD) ip_route->set = false; } } return true; } /* Route dump/allocation */ static void free_nh(nexthop_t *nh) { FREE_PTR(nh->addr); //#if HAVE_DECL_RTA_NEWDST // FREE_PTR(nh->as_to); //#endif FREE(nh); } static void free_nh_list(list_head_t *l) { nexthop_t *nh, *nh_tmp; list_for_each_entry_safe(nh, nh_tmp, l, e_list) free_nh(nh); } void free_iproute(ip_route_t *route) { FREE_PTR(route->dst); FREE_PTR(route->src); FREE_PTR(route->pref_src); FREE_PTR(route->via); free_nh_list(&route->nhs); #if HAVE_DECL_RTAX_CC_ALGO FREE_PTR(route->congctl); #endif FREE(route); } void free_iproute_list(list_head_t *l) { ip_route_t *route, *route_tmp; list_for_each_entry_safe(route, route_tmp, l, e_list) free_iproute(route); } #if HAVE_DECL_RTA_ENCAP #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS static size_t print_encap_mpls(char *op, size_t len, const encap_t* encap) { char *buf = op; const char* buf_end = op + len; unsigned i; op += snprintf(op, (size_t)(buf_end - op), " encap mpls"); /* LGTM does not seem to be able to recognise the op < buf_end -1 in the loop control */ for (i = 0; i < encap->mpls.num_labels && op < buf_end - 1; i++) op += snprintf(op, (size_t)(buf_end - op), "%s%x", i ? "/" : " ", ntohl(encap->mpls.addr[i].entry)); /* lgtm [cpp/overflowing-snprintf] */ return (size_t)(op - buf); } #endif static size_t print_encap_ip(char *op, size_t len, const encap_t* encap) { char *buf = op; const char *buf_end = op + len; op += snprintf(op, (size_t)(buf_end - op), " encap ip"); if (encap->flags & IPROUTE_BIT_ENCAP_ID) op += snprintf(op, (size_t)(buf_end - op), " id %" PRIu64, encap->ip.id); if (encap->ip.dst) op += snprintf(op, (size_t)(buf_end - op), " dst %s", ipaddresstos(NULL, encap->ip.dst)); if (encap->ip.src) op += snprintf(op, (size_t)(buf_end - op), " src %s", ipaddresstos(NULL, encap->ip.src)); if (encap->flags & IPROUTE_BIT_ENCAP_DSFIELD) op += snprintf(op, (size_t)(buf_end - op), " tos %d", encap->ip.tos); if (encap->flags & IPROUTE_BIT_ENCAP_TTL) op += snprintf(op, (size_t)(buf_end - op), " ttl %d", encap->ip.ttl); if (encap->flags & IPROUTE_BIT_ENCAP_FLAGS) op += snprintf(op, (size_t)(buf_end - op), " flags 0x%x", encap->ip.flags); return (size_t)(op - buf); } #if HAVE_DECL_LWTUNNEL_ENCAP_ILA static size_t print_encap_ila(char *op, size_t len, const encap_t* encap) { return (size_t)snprintf(op, len, " encap ila %" PRIu64, encap->ila.locator); } #endif static size_t print_encap_ip6(char *op, size_t len, const encap_t* encap) { char *buf = op; const char *buf_end = op + len; op += snprintf(op, (size_t)(buf_end - op), " encap ip6"); if (encap->flags & IPROUTE_BIT_ENCAP_ID) op += snprintf(op, (size_t)(buf_end - op), " id %" PRIu64, encap->ip6.id); if (encap->ip.dst) op += snprintf(op, (size_t)(buf_end - op), " dst %s", ipaddresstos(NULL, encap->ip6.dst)); if (encap->ip.src) op += snprintf(op, (size_t)(buf_end - op), " src %s", ipaddresstos(NULL, encap->ip6.src)); if (encap->flags & IPROUTE_BIT_ENCAP_DSFIELD) op += snprintf(op, (size_t)(buf_end - op), " tc %d", encap->ip6.tc); if (encap->flags & IPROUTE_BIT_ENCAP_HOPLIMIT) op += snprintf(op, (size_t)(buf_end - op), " hoplimit %d", encap->ip6.hoplimit); if (encap->flags & IPROUTE_BIT_ENCAP_FLAGS) op += snprintf(op, (size_t)(buf_end - op), " flags 0x%x", encap->ip6.flags); return (size_t)(op - buf); } static size_t print_encap(char *op, size_t len, const encap_t* encap) { switch (encap->type) { #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS case LWTUNNEL_ENCAP_MPLS: return print_encap_mpls(op, len, encap); #endif case LWTUNNEL_ENCAP_IP: return print_encap_ip(op, len, encap); #if HAVE_DECL_LWTUNNEL_ENCAP_ILA case LWTUNNEL_ENCAP_ILA: return print_encap_ila(op, len, encap); #endif case LWTUNNEL_ENCAP_IP6: return print_encap_ip6(op, len, encap); } return (size_t)snprintf(op, len, "unknown encap type %d", encap->type); } #endif void format_iproute(const ip_route_t *route, char *buf, size_t buf_len) { char *op = buf; const char *buf_end = buf + buf_len; nexthop_t *nh; interface_t *ifp; /* The do {...} while(false) loop is so that we can break out of the loop if the buffer is filled */ do { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%s ", route->mask & IPROUTE_BIT_ADD ? "add" : route->mask & IPROUTE_BIT_APPEND ? "append" : "prepend")) >= buf_end - 1) break; if (route->type != RTN_UNICAST) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%s ", get_rttables_rtntype(route->type))) >= buf_end - 1) break; if (route->dst) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%s", ipaddresstos(NULL, route->dst))) >= buf_end - 1) break; } else { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%s", "default")) >= buf_end - 1) break; } if (route->src) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " from %s", ipaddresstos(NULL, route->src))) >= buf_end - 1) break; //#if HAVE_DECL_RTA_NEWDST // /* MPLS only */ // if (route->as_to) // if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " as to %s", ipaddresstos(NULL, route->as_to))) >= buf_end - 1) // break; //#endif if (route->pref_src) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " src %s", ipaddresstos(NULL, route->pref_src))) >= buf_end - 1) break; if (route->mask & IPROUTE_BIT_DSFIELD) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " tos %u", route->tos)) >= buf_end - 1) break; #if HAVE_DECL_RTA_ENCAP if (route->encap.type != LWTUNNEL_ENCAP_NONE) if ((op += print_encap(op, (size_t)(buf_end - op), &route->encap)) >= buf_end - 1) break; #endif if (route->via) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " via %s %s", route->via->ifa.ifa_family == AF_INET6 ? "inet6" : "inet", ipaddresstos(NULL, route->via))) >= buf_end - 1) break; if (route->oif) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " dev %s", route->oif->ifname)) >= buf_end - 1) break; if (route->table != RT_TABLE_MAIN) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " table %u", route->table)) >= buf_end - 1) break; if (route->mask & IPROUTE_BIT_PROTOCOL) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " proto %u", route->protocol)) >= buf_end - 1) break; if (route->mask & IPROUTE_BIT_SCOPE) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " scope %u", route->scope)) >= buf_end - 1) break; if (route->mask & IPROUTE_BIT_METRIC) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " metric %u", route->metric)) >= buf_end - 1) break; if (route->family == AF_INET && route->flags & RTNH_F_ONLINK) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " %s", "onlink")) >= buf_end - 1) break; if (route->realms) { if (route->realms & 0xFFFF0000) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " realms %" PRIu32 "/", route->realms >> 16)) >= buf_end - 1) break; } else { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " realm ")) >= buf_end - 1) break; } if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%u", route->realms & 0xFFFF)) >= buf_end - 1) break; } #if HAVE_DECL_RTA_EXPIRES if (route->mask & IPROUTE_BIT_EXPIRES) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " expires %" PRIu32 "sec", route->expires)) >= buf_end - 1) break; #endif #if HAVE_DECL_RTAX_CC_ALGO if (route->congctl) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " congctl %s%s", route->congctl, route->lock & (1<= buf_end - 1) break; #endif if (route->mask & IPROUTE_BIT_RTT) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " %s%s ", "rtt", route->lock & (1<= buf_end - 1) break; if (route->rtt >= 8000) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%gs", route->rtt / (double)8000.0F)) >= buf_end - 1) break; } else { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%gms", route->rtt / (double)8.F)) >= buf_end - 1) break; } } if (route->mask & IPROUTE_BIT_RTTVAR) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " %s%s ", "rttvar", route->lock & (1<= buf_end - 1) break; if (route->rttvar >= 4000) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%gs", route->rttvar / (double)4000.0F)) >= buf_end - 1) break; } else { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%gms", route->rttvar / (double)4.F)) >= buf_end - 1) break; } } if (route->mask & IPROUTE_BIT_RTO_MIN) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " %s%s ", "rto_min", route->lock & (1<= buf_end - 1) break; if (route->rto_min >= 1000) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%gs", route->rto_min / (double)1000.0F)) >= buf_end - 1) break; } else { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%ums", route->rto_min)) >= buf_end - 1) break; } } if (route->features & RTAX_FEATURE_ECN) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " %s", "features ecn")) >= buf_end - 1) break; if (route->mask & IPROUTE_BIT_MTU) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " mtu %s%u", route->lock & (1<mtu)) >= buf_end - 1) break; } if (route->mask & IPROUTE_BIT_WINDOW) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " window %u", route->window)) >= buf_end - 1) break; if (route->mask & IPROUTE_BIT_SSTHRESH) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " ssthresh %s%u", route->lock & (1<ssthresh)) >= buf_end - 1) break; } if (route->mask & IPROUTE_BIT_CWND) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " cwnd %s%u", route->lock & (1<cwnd)) >= buf_end) break; } if (route->mask & IPROUTE_BIT_ADVMSS) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " advmss %s%u", route->lock & (1<advmss)) >= buf_end - 1) break; } if (route->mask & IPROUTE_BIT_REORDERING) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " reordering %s%u", route->lock & (1<reordering)) >= buf_end - 1) break; } if (route->mask & IPROUTE_BIT_HOPLIMIT) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " hoplimit %u", route->hoplimit)) >= buf_end - 1) break; if (route->mask & IPROUTE_BIT_INITCWND) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " initcwnd %u", route->initcwnd)) >= buf_end - 1) break; if (route->mask & IPROUTE_BIT_INITRWND) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " initrwnd %u", route->initrwnd)) >= buf_end - 1) break; #if HAVE_DECL_RTAX_QUICKACK if (route->mask & IPROUTE_BIT_QUICKACK) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " quickack %d", route->quickack)) >= buf_end - 1) break; #endif #if HAVE_DECL_RTA_PREF if (route->mask & IPROUTE_BIT_PREF) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " %s %s", "pref", route->pref == ICMPV6_ROUTER_PREF_LOW ? "low" : route->pref == ICMPV6_ROUTER_PREF_MEDIUM ? "medium" : route->pref == ICMPV6_ROUTER_PREF_HIGH ? "high" : "unknown")) >= buf_end - 1) break; #endif #if HAVE_DECL_RTAX_FASTOPEN_NO_COOKIE if (route->mask & IPROUTE_BIT_FASTOPEN_NO_COOKIE) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " %s %d", "fastopen_no_cookie", route->fastopen_no_cookie)) >= buf_end - 1) break; #endif #if HAVE_DECL_RTA_TTL_PROPAGATE if (route->mask & IPROUTE_BIT_TTL_PROPAGATE) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " %s %sabled", "ttl-propagate", route->ttl_propagate ? "en" : "dis")) >= buf_end - 1) break; #endif /* LGTM does not seem to be able to recognise the op < buf_end -1 break in the loop within a loop */ list_for_each_entry(nh, &route->nhs, e_list) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " nexthop")) >= buf_end - 1) /* lgtm [cpp/overflowing-snprintf] */ break; if (nh->addr) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " via inet%s %s" /* lgtm [cpp/overflowing-snprintf] */ , nh->addr->ifa.ifa_family == AF_INET ? "" : "6" , ipaddresstos(NULL,nh->addr))) >= buf_end - 1) break; if (nh->ifp) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " dev %s", nh->ifp->ifname)) >= buf_end - 1) /* lgtm [cpp/overflowing-snprintf] */ break; if (nh->mask & IPROUTE_BIT_WEIGHT) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " weight %d", nh->weight + 1)) >= buf_end - 1) /* lgtm [cpp/overflowing-snprintf] */ break; if (nh->flags & RTNH_F_ONLINK) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " onlink")) >= buf_end - 1) /* lgtm [cpp/overflowing-snprintf] */ break; if (nh->realms) { if (route->realms & 0xFFFF0000) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op) /* lgtm [cpp/overflowing-snprintf] */ , " realms %" PRIu32 "/", nh->realms >> 16)) >= buf_end - 1) break; } else { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " realm ")) >= buf_end - 1) /* lgtm [cpp/overflowing-snprintf] */ break; } if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), "%" PRIu32, nh->realms & 0xFFFF)) >= buf_end - 1) /* lgtm [cpp/overflowing-snprintf] */ break; } #if HAVE_DECL_RTA_ENCAP if (nh->encap.type != LWTUNNEL_ENCAP_NONE) if ((op += print_encap(op, (size_t)(buf_end - op), &nh->encap)) >= buf_end - 1) break; #endif } if (op >= buf_end - 1) break; if (route->dont_track) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " no_track")) >= buf_end - 1) break; if (route->track_group) if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " track_group %s", route->track_group->gname)) >= buf_end - 1) break; if (route->set && !route->dont_track && route->configured_ifindex && (!route->oif || route->oif->ifindex != route->configured_ifindex)) { if ((ifp = if_get_by_ifindex(route->configured_ifindex))) { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " [dev %s]", ifp->ifname)) >= buf_end - 1) break; } else { if ((op += (size_t)snprintf(op, (size_t)(buf_end - op), " [installed ifindex %" PRIu32 "]", route->configured_ifindex)) >= buf_end - 1) break; } } } while (false); } void dump_iproute(FILE *fp, const ip_route_t *route) { char *buf = MALLOC(ROUTE_BUF_SIZE); size_t len; size_t i; format_iproute(route, buf, ROUTE_BUF_SIZE); if (fp) conf_write(fp, "%*s%s", 5, "", buf); else { for (i = 0, len = strlen(buf); i < len; i += i ? MAX_LOG_MSG - 7 : MAX_LOG_MSG - 5) conf_write(fp, "%*s%s", i ? 6 : 5, "", buf + i); } FREE(buf); } void dump_iproute_list(FILE *fp, const list_head_t *l) { ip_route_t *route; list_for_each_entry(route, l, e_list) dump_iproute(fp, route); } #if HAVE_DECL_RTA_ENCAP #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS static int parse_encap_mpls(const vector_t *strvec, unsigned int *i_ptr, encap_t *encap) { const char *str; encap->type = LWTUNNEL_ENCAP_MPLS; if (*i_ptr >= vector_size(strvec)) { report_config_error(CONFIG_GENERAL_ERROR, "missing address for MPLS encapsulation"); return true; } str = strvec_slot(strvec, (*i_ptr)++); if (parse_mpls_address(str, &encap->mpls)) { report_config_error(CONFIG_GENERAL_ERROR, "invalid mpls address %s for encapsulation", str); return true; } return false; } #endif static int parse_encap_ip(const vector_t *strvec, unsigned int *i_ptr, encap_t *encap) { unsigned int i = *i_ptr; const char *str, *str1; encap->type = LWTUNNEL_ENCAP_IP; while (i + 1 < vector_size(strvec)) { str = strvec_slot(strvec, i); str1 = strvec_slot(strvec, i + 1); if (!strcmp(str, "id")) { if (get_u64(&encap->ip.id, str1, UINT64_MAX, "encap id %s value is invalid")) goto err; encap->flags |= IPROUTE_BIT_ENCAP_ID; } else if (!strcmp(str, "dst")) { if (encap->ip.dst) FREE_PTR(encap->ip.dst); encap->ip.dst = parse_ipaddress(NULL, str1, false); if (!encap->ip.dst) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid encap ip dst %s", str1); goto err; } if (encap->ip.dst->ifa.ifa_family != AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "IPv6 address %s not valid for ip encapsulation", str1); goto err; } } else if (!strcmp(str, "src")) { if (encap->ip.src) FREE_PTR(encap->ip.src); encap->ip.src = parse_ipaddress(NULL, str1, false); if (!encap->ip.src) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid encap ip src %s", str1); goto err; } if (encap->ip.src->ifa.ifa_family != AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "IPv6 address %s not valid for ip encapsulation", str1); goto err; } } else if (!strcmp(str, "tos")) { if (!find_rttables_dsfield(str1, &encap->ip.tos)) { report_config_error(CONFIG_GENERAL_ERROR, "dsfield %s not valid for ip encapsulation", str1); goto err; } encap->flags |= IPROUTE_BIT_ENCAP_DSFIELD; } else if (!strcmp(str, "ttl")) { if (get_u8(&encap->ip.ttl, str1, UINT8_MAX, "ttl %s is not valid for ip encapsulation")) goto err; encap->flags |= IPROUTE_BIT_ENCAP_TTL; } else if (!strcmp(str, "flags")) { if (get_u16(&encap->ip.flags, str1, UINT16_MAX, "flags %s is not valid for ip encapsulation")) goto err; encap->flags |= IPROUTE_BIT_ENCAP_FLAGS; } else break; i += 2; } if (!encap->ip.dst && !(encap->flags & IPROUTE_BIT_ENCAP_ID)) { report_config_error(CONFIG_GENERAL_ERROR, "address or id missing for ip encapsulation"); goto err; } *i_ptr = i; return false; err: *i_ptr = i; if (encap->ip.dst) { FREE_PTR(encap->ip.dst); encap->ip.dst = NULL; } if (encap->ip.src){ FREE_PTR(encap->ip.src); encap->ip.src = NULL; } return true; } #if HAVE_DECL_LWTUNNEL_ENCAP_ILA static int parse_encap_ila(const vector_t *strvec, unsigned int *i_ptr, encap_t *encap) { const char *str; encap->type = LWTUNNEL_ENCAP_ILA; if (*i_ptr >= vector_size(strvec)) { report_config_error(CONFIG_GENERAL_ERROR, "missing locator for ILA encapsulation"); return true; } str = strvec_slot(strvec, (*i_ptr)++); if (get_addr64(&encap->ila.locator, str)) { report_config_error(CONFIG_GENERAL_ERROR, "invalid locator %s for ila encapsulation", str); return true; } return false; } #endif static int parse_encap_ip6(const vector_t *strvec, unsigned int *i_ptr, encap_t *encap) { unsigned int i = *i_ptr; const char *str, *str1; encap->type = LWTUNNEL_ENCAP_IP6; while (i + 1 < vector_size(strvec)) { str = strvec_slot(strvec, i); str1 = strvec_slot(strvec, i + 1); if (!strcmp(str, "id")) { if (get_u64(&encap->ip6.id, str1, UINT64_MAX, "id %s value invalid for IPv6 encapsulation")) goto err; encap->flags |= IPROUTE_BIT_ENCAP_ID; } else if (!strcmp(str, "dst")) { if (encap->ip6.dst) FREE_PTR(encap->ip6.dst); encap->ip6.dst = parse_ipaddress(NULL, str1, false); if (!encap->ip6.dst) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid encap ip6 dst %s", str1); goto err; } if (encap->ip6.dst->ifa.ifa_family != AF_INET6) { report_config_error(CONFIG_GENERAL_ERROR, "IPv4 address %s not valid for ip6 encapsulation", str1); goto err; } } else if (!strcmp(str, "src")) { if (encap->ip6.src) FREE_PTR(encap->ip6.src); encap->ip6.src = parse_ipaddress(NULL, str1, false); if (!encap->ip6.src) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid encap ip6 src %s", str1); goto err; } if (encap->ip6.src->ifa.ifa_family != AF_INET6) { report_config_error(CONFIG_GENERAL_ERROR, "IPv4 address %s not valid for ip6 encapsulation", str1); goto err; } } else if (!strcmp(str, "tc")) { if (!find_rttables_dsfield(str1, &encap->ip6.tc)) { report_config_error(CONFIG_GENERAL_ERROR, "tc value %s is invalid for ip6 encapsulation", str); goto err; } encap->flags |= IPROUTE_BIT_ENCAP_DSFIELD; } else if (!strcmp(str, "hoplimit")) { if (get_u8(&encap->ip6.hoplimit, str1, UINT8_MAX, "Invalid hoplimit %s specified for ip6 encapsulation")) goto err; encap->flags |= IPROUTE_BIT_ENCAP_HOPLIMIT; } else if (!strcmp(str, "flags")) { if (get_u16(&encap->ip6.flags, str1, UINT16_MAX, "flags %s is not valid for ip6 encapsulation")) goto err; encap->flags |= IPROUTE_BIT_ENCAP_FLAGS; } else break; i += 2; } if (!encap->ip.dst && !(encap->flags & IPROUTE_BIT_ENCAP_ID)) { report_config_error(CONFIG_GENERAL_ERROR, "address or id missing for ip6 encapsulation"); goto err; } *i_ptr = i; return false; err: *i_ptr = i; if (encap->ip6.dst) { FREE_PTR(encap->ip6.dst); encap->ip6.dst = NULL; } if (encap->ip6.src) { FREE_PTR(encap->ip6.src); encap->ip6.src = NULL; } return true; } static bool parse_encap(const vector_t *strvec, unsigned int *i, encap_t *encap) { const char *str; if (vector_size(strvec) <= ++*i) { report_config_error(CONFIG_GENERAL_ERROR, "Missing encap type"); return false; } str = strvec_slot(strvec, (*i)++); if (!strcmp(str, "ip")) parse_encap_ip(strvec, i, encap); else if (!strcmp(str, "ip6")) parse_encap_ip6(strvec, i, encap); #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS else if (!strcmp(str, "mpls")) parse_encap_mpls(strvec, i, encap); #endif #if HAVE_DECL_LWTUNNEL_ENCAP_ILA else if (!strcmp(str, "ila")) parse_encap_ila(strvec, i, encap); #endif else { report_config_error(CONFIG_GENERAL_ERROR, "Unknown encap type - %s", str); return false; } --*i; return true; } #endif static void parse_nexthops(const vector_t *strvec, unsigned int i, ip_route_t *route) { uint8_t family = AF_UNSPEC; nexthop_t *new; const char *str; uint32_t val; while (i < vector_size(strvec) && !strcmp("nexthop", strvec_slot(strvec, i))) { i++; new = MALLOC(sizeof(nexthop_t)); INIT_LIST_HEAD(&new->e_list); while (i < vector_size(strvec)) { str = strvec_slot(strvec, i); if (!strcmp(str, "via")) { str = strvec_slot(strvec, ++i); if (!strcmp(str, "inet")) { family = AF_INET; str = strvec_slot(strvec, ++i); } else if (!strcmp(str, "inet6")) { family = AF_INET6; str = strvec_slot(strvec, ++i); } if (family != AF_UNSPEC) { if (route->family == AF_UNSPEC) route->family = family; else if (route->family != family) { report_config_error(CONFIG_GENERAL_ERROR, "IPv4/6 mismatch for nexthop"); goto err; } } new->addr = parse_ipaddress(NULL, str, false); if (!new->addr) { report_config_error(CONFIG_GENERAL_ERROR, "invalid nexthop address %s", str); goto err; } if (route->family != AF_UNSPEC && new->addr->ifa.ifa_family != route->family) { report_config_error(CONFIG_GENERAL_ERROR, "Address family mismatch for next hop"); goto err; } if (route->family == AF_UNSPEC) route->family = new->addr->ifa.ifa_family; } else if (!strcmp(str, "dev")) { str = strvec_slot(strvec, ++i); new->ifp = if_get_by_ifname(str, IF_CREATE_IF_DYNAMIC); if (!new->ifp) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - interface %s for VROUTE nexthop doesn't exist", str); goto err; } } else if (!strcmp(str, "weight")) { if (get_u32(&val, strvec_slot(strvec, ++i), 256, "Invalid weight %s specified for route")) goto err; if (!val) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid weight 0 specified for route"); goto err; } new->weight = (uint8_t)(--val & 0xff); new->mask |= IPROUTE_BIT_WEIGHT; } else if (!strcmp(str, "onlink")) { /* Note: IPv4 only */ new->flags |= RTNH_F_ONLINK; } else if (!strcmp(str, "encap")) { // New in 4.4 #if HAVE_DECL_RTA_ENCAP parse_encap(strvec, &i, &new->encap); #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "encap"); #endif } else if (!strcmp(str, "realms")) { /* Note: IPv4 only */ if (get_realms(&new->realms, strvec_slot(strvec, ++i))) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid realms %s for route", strvec_slot(strvec,i)); goto err; } if (route->family == AF_UNSPEC) route->family = AF_INET; else if (route->family != AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "realms are only supported for IPv4"); goto err; } } else if (!strcmp(str, "as")) { if (!strcmp("to", strvec_slot(strvec, ++i))) i++; report_config_error(CONFIG_GENERAL_ERROR, "'as [to]' (nat) not supported"); goto err; } else break; i++; } list_add_tail(&new->e_list, &route->nhs); new = NULL; } if (i < vector_size(strvec)) { report_config_error(CONFIG_GENERAL_ERROR, "Route has trailing nonsense after nexthops - %s", strvec_slot(strvec, i)); goto err; } return; err: FREE_PTR(new); } void alloc_route(list_head_t *rt_list, const vector_t *strvec, bool allow_track_group) { ip_route_t *new; interface_t *ifp; const char *str; uint32_t val; uint8_t val8; unsigned int i = 0; bool do_nexthop = false; uint8_t family; const char *dest = NULL; PMALLOC(new); if (!new) { log_message(LOG_INFO, "Unable to allocate new ip_route"); return; } INIT_LIST_HEAD(&new->e_list); INIT_LIST_HEAD(&new->nhs); new->table = RT_TABLE_MAIN; new->scope = RT_SCOPE_UNIVERSE; new->type = RTN_UNICAST; new->family = AF_UNSPEC; /* FMT parse */ while (i < vector_size(strvec)) { str = strvec_slot(strvec, i); /* cmd parsing */ if (!strcmp(str, "inet6")) { if (new->family == AF_UNSPEC) new->family = AF_INET6; else if (new->family != AF_INET6) { report_config_error(CONFIG_GENERAL_ERROR, "inet6 specified for IPv4 route"); goto err; } i++; } else if (!strcmp(str, "inet")) { if (new->family == AF_UNSPEC) new->family = AF_INET; else if (new->family != AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "inet specified for IPv6 route"); goto err; } i++; } else if (!strcmp(str, "src")) { if (new->pref_src) FREE(new->pref_src); new->pref_src = parse_ipaddress(NULL, strvec_slot(strvec, ++i), false); if (!new->pref_src) { report_config_error(CONFIG_GENERAL_ERROR, "invalid route src address %s", strvec_slot(strvec, i)); goto err; } if (new->family == AF_UNSPEC) new->family = new->pref_src->ifa.ifa_family; else if (new->family != new->pref_src->ifa.ifa_family) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot mix IPv4 and IPv6 addresses for route"); goto err; } } else if (!strcmp(str, "as")) { if (!strcmp("to", strvec_slot(strvec, ++i))) i++; #if HAVE_DECL_RTA_NEWDST report_config_error(CONFIG_GENERAL_ERROR, "\"as to\" for MPLS only - ignoring"); #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "'as [to]'"); #endif } else if (!strcmp(str, "via") || !strcmp(str, "gw")) { /* "gw" maintained for backward keepalived compatibility */ if (str[0] == 'g') /* "gw" */ report_config_error(CONFIG_GENERAL_ERROR, "\"gw\" for routes is deprecated. Please use \"via\""); str = strvec_slot(strvec, ++i); if (!strcmp(str, "inet")) { family = AF_INET; str = strvec_slot(strvec, ++i); } else if (!strcmp(str, "inet6")) { family = AF_INET6; str = strvec_slot(strvec, ++i); } else family = new->family; if (new->family == AF_UNSPEC) new->family = family; else if (new->family != family) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot mix IPv4 and IPv6 addresses for route"); goto err; } if (new->via) FREE(new->via); new->via = parse_ipaddress(NULL, str, false); if (!new->via) { report_config_error(CONFIG_GENERAL_ERROR, "invalid route via address %s", strvec_slot(strvec, i)); goto err; } if (new->family == AF_UNSPEC) new->family = new->via->ifa.ifa_family; else if (new->family != new->via->ifa.ifa_family) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot mix IPv4 and IPv6 addresses for route"); goto err; } } else if (!strcmp(str, "from")) { if (new->src) FREE(new->src); new->src = parse_route(strvec_slot(strvec, ++i)); if (!new->src) { report_config_error(CONFIG_GENERAL_ERROR, "invalid route from address %s", strvec_slot(strvec, i)); goto err; } if (new->src->ifa.ifa_family != AF_INET6) { report_config_error(CONFIG_GENERAL_ERROR, "route from address only supported with IPv6 (%s)", strvec_slot(strvec, i)); goto err; } if (new->family == AF_UNSPEC) new->family = new->src->ifa.ifa_family; else if (new->family != new->src->ifa.ifa_family) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot mix IPv4 and IPv6 addresses for route"); goto err; } } else if (!strcmp(str, "tos") || !strcmp(str,"dsfield")) { /* Note: IPv4 only */ if (!find_rttables_dsfield(strvec_slot(strvec, ++i), &val8)) { report_config_error(CONFIG_GENERAL_ERROR, "TOS value %s is invalid", strvec_slot(strvec, i)); goto err; } new->tos = val8; new->mask |= IPROUTE_BIT_DSFIELD; } else if (!strcmp(str, "table")) { if (!find_rttables_table(strvec_slot(strvec, ++i), &val)) { report_config_error(CONFIG_GENERAL_ERROR, "Routing table %s not found for route", strvec_slot(strvec, i)); goto err; } new->table = val; } else if (!strcmp(str, "protocol")) { if (!find_rttables_proto(strvec_slot(strvec, ++i), &val8)) { report_config_error(CONFIG_GENERAL_ERROR, "Protocol %s not found or invalid for route", strvec_slot(strvec, i)); goto err; } new->protocol = val8; new->mask |= IPROUTE_BIT_PROTOCOL; } else if (!strcmp(str, "scope")) { /* Note: IPv4 only */ if (!find_rttables_scope(strvec_slot(strvec, ++i), &val8)) { report_config_error(CONFIG_GENERAL_ERROR, "Scope %s not found or invalid for route", strvec_slot(strvec, i)); goto err; } new->scope = val8; new->mask |= IPROUTE_BIT_SCOPE; } else if (!strcmp(str, "metric") || !strcmp(str, "priority") || !strcmp(str, "preference")) { if (get_u32(&new->metric, strvec_slot(strvec, ++i), UINT32_MAX, "Invalid metric %s specified for route")) goto err; new->mask |= IPROUTE_BIT_METRIC; } else if (!strcmp(str, "dev") || !strcmp(str, "oif")) { str = strvec_slot(strvec, ++i); ifp = if_get_by_ifname(str, IF_CREATE_IF_DYNAMIC); if (!ifp) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - interface %s for VROUTE nexthop doesn't exist", str); goto err; } new->oif = ifp; } else if (!strcmp(str, "onlink")) { /* Note: IPv4 only */ new->flags |= RTNH_F_ONLINK; } else if (!strcmp(str, "encap")) { // New in 4.4 #if HAVE_DECL_RTA_ENCAP parse_encap(strvec, &i, &new->encap); #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "encap"); #endif } else if (!strcmp(str, "expires")) { // New in 4.4 i++; #if HAVE_DECL_RTA_EXPIRES if (new->family == AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "expires is only valid for IPv6"); goto err; } new->family = AF_INET6; if (get_u32(&new->expires, strvec_slot(strvec, i), UINT32_MAX, "Invalid expires time %s specified for route")) goto err; new->mask |= IPROUTE_BIT_EXPIRES; #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "expires"); #endif } else if (!strcmp(str, "mtu")) { if (!strcmp(strvec_slot(strvec, ++i), "lock")) { new->lock |= 1 << RTAX_MTU; i++; } if (get_u32(&new->mtu, strvec_slot(strvec, i), UINT32_MAX, "Invalid MTU %s specified for route")) goto err; new->mask |= IPROUTE_BIT_MTU; } else if (!strcmp(str, "hoplimit")) { if (get_u8(&val8, strvec_slot(strvec, ++i), 255, "Invalid hoplimit %s specified for route")) goto err; new->hoplimit = val8; new->mask |= IPROUTE_BIT_HOPLIMIT; } else if (!strcmp(str, "advmss")) { if (!strcmp(strvec_slot(strvec, ++i), "lock")) { new->lock |= 1 << RTAX_ADVMSS; i++; } if (get_u32(&new->advmss, strvec_slot(strvec, i), UINT32_MAX, "Invalid advmss %s specified for route")) goto err; new->mask |= IPROUTE_BIT_ADVMSS; } else if (!strcmp(str, "rtt")) { if (!strcmp(strvec_slot(strvec, ++i), "lock")) { new->lock |= 1 << RTAX_RTT; i++; } if (get_time_rtt(&new->rtt, strvec_slot(strvec, i), 8, "rtt")) { /* Units are 1/8000 second */ report_config_error(CONFIG_GENERAL_ERROR, "Invalid rtt %s for route", strvec_slot(strvec,i)); goto err; } new->mask |= IPROUTE_BIT_RTT; } else if (!strcmp(str, "rttvar")) { if (!strcmp(strvec_slot(strvec, ++i), "lock")) { new->lock |= 1 << RTAX_RTTVAR; i++; } if (get_time_rtt(&new->rttvar, strvec_slot(strvec, i), 4, "rtt")) { /* Units are 1/4000 second */ report_config_error(CONFIG_GENERAL_ERROR, "Invalid rttvar %s for route", strvec_slot(strvec,i)); goto err; } new->mask |= IPROUTE_BIT_RTTVAR; } else if (!strcmp(str, "reordering")) { if (!strcmp(strvec_slot(strvec, ++i), "lock")) { new->lock |= 1 << RTAX_REORDERING; i++; } if (get_u32(&new->reordering, strvec_slot(strvec, i), UINT32_MAX, "Invalid reordering value %s specified for route")) goto err; new->mask |= IPROUTE_BIT_REORDERING; } else if (!strcmp(str, "window")) { if (get_u32(&new->window, strvec_slot(strvec, ++i), UINT32_MAX, "Invalid window value %s specified for route")) goto err; new->mask |= IPROUTE_BIT_WINDOW; } else if (!strcmp(str, "cwnd")) { if (!strcmp(strvec_slot(strvec, ++i), "lock")) { new->lock |= 1 << RTAX_CWND; i++; } if (get_u32(&new->cwnd, strvec_slot(strvec, i), UINT32_MAX, "Invalid cwnd value %s specified for route")) goto err; new->mask |= IPROUTE_BIT_CWND; } else if (!strcmp(str, "ssthresh")) { if (!strcmp(strvec_slot(strvec, ++i), "lock")) { new->lock |= 1 << RTAX_SSTHRESH; i++; } if (get_u32(&new->ssthresh, strvec_slot(strvec, i), UINT32_MAX, "Invalid ssthresh value %s specified for route")) goto err; new->mask |= IPROUTE_BIT_SSTHRESH; } else if (!strcmp(str, "realms")) { if (get_realms(&new->realms, strvec_slot(strvec, ++i))) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid realms %s for route", strvec_slot(strvec,i)); goto err; } if (new->family == AF_INET6) { report_config_error(CONFIG_GENERAL_ERROR, "realms are only valid for IPv4"); goto err; } new->family = AF_INET; } else if (!strcmp(str, "rto_min")) { if (!strcmp(strvec_slot(strvec, ++i), "lock")) { new->lock |= 1 << RTAX_RTO_MIN; i++; } if (get_time_rtt(&new->rto_min, strvec_slot(strvec, i), 1, "rto")) { /* Units are 1/1000 second */ report_config_error(CONFIG_GENERAL_ERROR, "Invalid rto_min value %s specified for route", strvec_slot(strvec, i)); goto err; } new->mask |= IPROUTE_BIT_RTO_MIN; } else if (!strcmp(str, "initcwnd")) { if (get_u32(&new->initcwnd, strvec_slot(strvec, ++i), UINT32_MAX, "Invalid initcwnd value %s specified for route")) goto err; new->mask |= IPROUTE_BIT_INITCWND; } else if (!strcmp(str, "initrwnd")) { i++; if (get_u32(&new->initrwnd, strvec_slot(strvec, i), UINT32_MAX, "Invalid initrwnd value %s specified for route")) goto err; new->mask |= IPROUTE_BIT_INITRWND; } else if (!strcmp(str, "features")) { i++; if (!strcmp("ecn", strvec_slot(strvec, i))) new->features |= RTAX_FEATURE_ECN; else report_config_error(CONFIG_GENERAL_ERROR, "feature %s not supported", strvec_slot(strvec,i)); } else if (!strcmp(str, "quickack")) { i++; #if HAVE_DECL_RTAX_QUICKACK if (get_u32(&val, strvec_slot(strvec, i), 1, "Invalid quickack value %s specified for route")) goto err; new->quickack = val; new->mask |= IPROUTE_BIT_QUICKACK; #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "quickack for route"); #endif } else if (!strcmp(str, "congctl")) { i++; #if HAVE_DECL_RTAX_CC_ALGO if (!strcmp(strvec_slot(strvec, i), "lock")) { new->lock |= 1 << RTAX_CC_ALGO; i++; } str = strvec_slot(strvec, i); new->congctl = STRDUP(str); #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "congctl for route"); #endif } else if (!strcmp(str, "pref")) { i++; #if HAVE_DECL_RTA_PREF if (new->family == AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "pref is only valid for IPv6"); goto err; } new->family = AF_INET6; str = strvec_slot(strvec, i); if (!strcmp(str, "low")) new->pref = ICMPV6_ROUTER_PREF_LOW; else if (!strcmp(str, "medium")) new->pref = ICMPV6_ROUTER_PREF_MEDIUM; else if (!strcmp(str, "high")) new->pref = ICMPV6_ROUTER_PREF_HIGH; else if (!get_u8(&val8, str, UINT8_MAX, "Invalid pref value %s specified for route")) new->pref = val8; else goto err; new->mask |= IPROUTE_BIT_PREF; #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "pref"); #endif } else if (!strcmp(str, "ttl-propagate")) { i++; #if HAVE_DECL_RTA_TTL_PROPAGATE str = strvec_slot(strvec, i); if (!strcmp(str, "enabled")) new->ttl_propagate = 1; else if (!strcmp(str, "disabled")) new->ttl_propagate = 0; else report_config_error(CONFIG_GENERAL_ERROR, "%s value %s not recognised", "ttl-propagate", str); new->mask |= IPROUTE_BIT_TTL_PROPAGATE; #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "ttl-propagate"); #endif } else if (!strcmp(str, "fastopen_no_cookie")) { i++; #if HAVE_DECL_RTAX_FASTOPEN_NO_COOKIE if (get_u32(&val, strvec_slot(strvec, i), 1, "Invalid fastopen_no_cookie value %s specified for route")) goto err; new->fastopen_no_cookie = !!val; new->mask |= IPROUTE_BIT_FASTOPEN_NO_COOKIE; #else report_config_error(CONFIG_GENERAL_ERROR, "%s not supported by kernel", "fastopen_no_cookie"); #endif } else if (!strcmp(str, "add")) { /* This is the default for iproute2 */ new->mask |= IPROUTE_BIT_ADD; new->mask &= ~IPROUTE_BIT_APPEND; } else if (!strcmp(str, "prepend")) { /* This is the default for the kernel and is the traditional keepalived behaviour */ new->mask &= ~( IPROUTE_BIT_ADD | IPROUTE_BIT_APPEND); } else if (!strcmp(str, "append")) { new->mask |= IPROUTE_BIT_APPEND; new->mask &= ~IPROUTE_BIT_ADD; } /* Maintained for backward compatibility */ else if (!strcmp(str, "or")) { report_config_error(CONFIG_GENERAL_ERROR, "\"or\" for routes is deprecated. Please use \"nexthop\""); if (!list_empty(&new->nhs)) { report_config_error(CONFIG_GENERAL_ERROR, "\"or\" route already specified - ignoring subsequent"); i += 2; continue; } /* Transfer the via address to the first nexthop */ nexthop_t *nh = MALLOC(sizeof(nexthop_t)); INIT_LIST_HEAD(&nh->e_list); nh->addr = new->via; new->via = NULL; list_add_tail(&nh->e_list, &new->nhs); /* Now handle the "or" address */ nh = MALLOC(sizeof(nexthop_t)); INIT_LIST_HEAD(&nh->e_list); nh->addr = parse_ipaddress(NULL, strvec_slot(strvec, ++i), false); if (!nh->addr) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid \"or\" address %s" , strvec_slot(strvec, i)); FREE(nh); goto err; } list_add_tail(&nh->e_list, &new->nhs); } else if (!strcmp(str, "nexthop")) { if (!list_empty(&new->nhs)) report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify nexthops with \"or\" route"); else do_nexthop = true; break; } else if (!strcmp(str, "no_track")) new->dont_track = true; else if (allow_track_group && !strcmp(str, "track_group")) { i++; if (new->track_group) { report_config_error(CONFIG_GENERAL_ERROR, "track_group %s is a duplicate", strvec_slot(strvec, i)); break; } if (!(new->track_group = static_track_group_find(strvec_slot(strvec, i)))) report_config_error(CONFIG_GENERAL_ERROR, "track_group %s not found", strvec_slot(strvec, i)); } #ifdef _HAVE_VRF_ else if (!strcmp(str, "vrf")) { if (!(ifp = if_get_by_ifname(strvec_slot(strvec, ++i), IF_NO_CREATE))) { report_config_error(CONFIG_GENERAL_ERROR, "VRF %s not found for route", strvec_slot(strvec, i)); goto err; } if (ifp->if_type != IF_TYPE_VRF) { report_config_error(CONFIG_GENERAL_ERROR, "Route specified VRF %s is not a VRF", strvec_slot(strvec, i)); goto err; } new->table = ifp->vrf_tb_id; } #endif else { if (!strcmp(str, "to")) i++; if (find_rttables_rtntype(str, &val8)) { new->type = val8; new->mask |= IPROUTE_BIT_TYPE; i++; } if (new->dst) FREE(new->dst); dest = strvec_slot(strvec, i); new->dst = parse_route(dest); if (!new->dst) { report_config_error(CONFIG_GENERAL_ERROR, "unknown route keyword %s", dest); goto err; } if (new->family == AF_UNSPEC) new->family = new->dst->ifa.ifa_family; else if (new->family != new->dst->ifa.ifa_family) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot mix IPv4 and IPv6 addresses for route (%s)", dest); goto err; } } i++; } if (do_nexthop) parse_nexthops(strvec, i, new); else if (i < vector_size(strvec)) { report_config_error(CONFIG_GENERAL_ERROR, "Route has trailing nonsense - %s", strvec_slot(strvec, i)); goto err; } if (!new->dst) { report_config_error(CONFIG_GENERAL_ERROR, "Route must have a destination"); goto err; } if (!new->dont_track) { if ((new->mask & IPROUTE_BIT_PROTOCOL) && new->protocol != RTPROT_KEEPALIVED) report_config_error(CONFIG_GENERAL_ERROR, "Route cannot be tracked if protocol is not RTPROT_KEEPALIVED(%d), resetting protocol", RTPROT_KEEPALIVED); new->protocol = RTPROT_KEEPALIVED; new->mask |= IPROUTE_BIT_PROTOCOL; } if (new->track_group && !new->oif) { report_config_error(CONFIG_GENERAL_ERROR, "Static route cannot have track group if no oif specified"); new->track_group = NULL; } /* Check that family is set */ if (new->family == AF_UNSPEC) new->family = AF_INET; if (new->dst->ifa.ifa_family == AF_UNSPEC) new->dst->ifa.ifa_family = new->family; if (new->src && new->src->ifa.ifa_family == AF_UNSPEC) new->src->ifa.ifa_family = new->family; list_add_tail(&new->e_list, rt_list); return; err: free_iproute(new); } static bool __attribute__ ((pure)) compare_nexthops(const list_head_t *a, const list_head_t *b) { nexthop_t *nh_a; nexthop_t *nh_b; if (list_empty(a) != list_empty(b)) return false; if (list_empty(a)) return true; nh_b = list_first_entry(b, nexthop_t, e_list); list_for_each_entry(nh_a, a, e_list) { if (list_is_last(&nh_a->e_list, a) != list_is_last(&nh_b->e_list, b)) return false; /* Do some comparisons */ if (nh_a->mask != nh_b->mask || compare_ipaddress(nh_a->addr, nh_b->addr) || nh_a->ifp != nh_b->ifp || nh_a->weight != nh_b->weight || nh_a->flags != nh_b->flags || nh_a->realms != nh_b->realms) return false; #if HAVE_DECL_RTA_ENCAP if (nh_a->encap.type != nh_b->encap.type || nh_a->encap.flags != nh_b->encap.flags) return false; if (nh_a->encap.type == LWTUNNEL_ENCAP_NONE) { /* Don't keep checking encap type if none */ } else if (nh_a->encap.type == LWTUNNEL_ENCAP_IP) { if (nh_a->encap.ip.id != nh_b->encap.ip.id || compare_ipaddress(nh_a->encap.ip.dst, nh_b->encap.ip.dst) || compare_ipaddress(nh_a->encap.ip.src, nh_b->encap.ip.src) || nh_a->encap.ip.tos != nh_b->encap.ip.tos || nh_a->encap.ip.flags != nh_b->encap.ip.flags || nh_a->encap.ip.ttl != nh_b->encap.ip.ttl) return false; } else if (nh_a->encap.type == LWTUNNEL_ENCAP_IP6) { if (nh_a->encap.ip6.id != nh_b->encap.ip6.id || compare_ipaddress(nh_a->encap.ip6.dst, nh_b->encap.ip6.dst) || compare_ipaddress(nh_a->encap.ip6.src, nh_b->encap.ip6.src) || nh_a->encap.ip6.tc != nh_b->encap.ip6.tc || nh_a->encap.ip6.flags != nh_b->encap.ip6.flags || nh_a->encap.ip6.hoplimit != nh_b->encap.ip6.hoplimit) return false; } #if HAVE_DECL_LWTUNNEL_ENCAP_ILA else if (nh_a->encap.type == LWTUNNEL_ENCAP_ILA) { if (nh_a->encap.ila.locator != nh_b->encap.ila.locator) return false; } #endif #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS else if (nh_a->encap.type == LWTUNNEL_ENCAP_MPLS) { size_t label; if (nh_a->encap.mpls.num_labels != nh_b->encap.mpls.num_labels) return false; for (label = 0; label < nh_a->encap.mpls.num_labels; label++) { if (nh_a->encap.mpls.addr[label].entry != nh_b->encap.mpls.addr[label].entry) return false; } } #endif #endif if (list_is_last(&nh_b->e_list, b)) return true; nh_b = list_first_entry(&nh_b->e_list, nexthop_t, e_list); } /* NOT REACHED */ return false; } /* Try to find a route in a list */ static ip_route_t * route_exist(list_head_t *l, ip_route_t *route) { ip_route_t *ip_route; list_for_each_entry(ip_route, l, e_list) { /* The kernel's key to a route is (to, tos, preference, table), * but since we don't specify NLM_F_EXCL when adding a route we * also need to check via/nexthops, scope and type. */ if (!compare_ipaddress(ip_route->dst, route->dst) && ip_route->dst->ifa.ifa_prefixlen == route->dst->ifa.ifa_prefixlen && ip_route->tos == route->tos && (!((ip_route->mask ^ route->mask) & IPROUTE_BIT_METRIC)) && (!(ip_route->mask & IPROUTE_BIT_METRIC) || ip_route->metric == route->metric) && ip_route->table == route->table && ip_route->scope == route->scope && ip_route->type == route->type && !ip_route->via == !route->via && (!ip_route->via || !compare_ipaddress(ip_route->via, route->via)) && ip_route->oif == route->oif && compare_nexthops(&ip_route->nhs, &route->nhs)) { ip_route->set = route->set; return ip_route; } } return NULL; } /* Clear diff routes */ void clear_diff_routes(list_head_t *l, list_head_t *n) { ip_route_t *route, *new_route; /* No route in previous conf */ if (list_empty(l)) return; /* All routes removed */ if (list_empty(n)) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Removing a VirtualRoute block"); netlink_rtlist(l, IPROUTE_DEL, false); return; } list_for_each_entry(route, l, e_list) { if (route->set) { if (!(new_route = route_exist(n, route))) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Removing route %s" , ipaddresstos(NULL, route->dst)); netlink_route(route, IPROUTE_DEL); continue; } /* There are too many route options to compare to see if the * routes are the same or not, so just replace the existing route * with the new one. * We try replacing the route, but if, for example, it has a src * address that is a new VIP, then the route won't be able to be * added (replaced) now. In this case delete the old route, mark * it as not set, and then it will be added later when any new * routes are added. */ netlink_error_ignore = EINVAL; if (netlink_route(new_route, IPROUTE_REPLACE)) { netlink_error_ignore = 0; netlink_route(route, IPROUTE_DEL); new_route->set = false; } else netlink_error_ignore = 0; } } } /* Diff conf handler */ void clear_diff_static_routes(void) { clear_diff_routes(&old_vrrp_data->static_routes, &vrrp_data->static_routes); } void reinstate_static_route(ip_route_t *route) { char buf[256]; route->set = !netlink_route(route, IPROUTE_ADD); format_iproute(route, buf, sizeof(buf)); log_message(LOG_INFO, "Restoring deleted static route %s", buf); } keepalived-2.3.3/keepalived/vrrp/vrrp_ipset.c0000664000175000017500000003461314460043445014771 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: ipset manipulation used in conjunction with iptables * * Author: Quentin Armitage, * * 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, */ /* We dynamically attempt to load the library "by hand", since keepalived * may have been built on a system with ipsets, but the target system may * not have the ipset libraries installed. * * If the ipset libraries are not installed, keepalived will fallback to * adding entries into iptables. */ #include "config.h" #include /* Force inclusion of net/if.h before linux/if.h */ #define LIBIPSET_NFPROTO_H #define LIBIPSET_NF_INET_ADDR_H #include #include #ifndef LIBIPSET_PRE_V7_COMPAT #include #endif #include #include /* For __beXX types in userland */ #include /* For nf_inet_addr */ #include #include "logger.h" #include "global_data.h" #include "vrrp_ipset.h" #include "vrrp_firewall.h" #include "vrrp_iptables_calls.h" #include "main.h" #include "utils.h" #ifdef _LIBIPSET_DYNAMIC_ #include /* The addresses of the functions we want */ #ifdef LIBIPSET_PRE_V7_COMPAT struct ipset_session* (*ipset_session_init_addr)(ipset_outfn outfn); #else struct ipset_session* (*ipset_session_init_addr)(ipset_print_outfn outfn, const void *p); #endif int (*ipset_session_fini_addr)(struct ipset_session *session); struct ipset_data* (*ipset_session_data_addr)(const struct ipset_session *session); #ifdef LIBIPSET_PRE_V7_COMPAT int (*ipset_envopt_parse_addr)(struct ipset_session *session, int env, const char *str); #else void (*ipset_envopt_set_addr)(struct ipset_session *session, int env); #endif const struct ipset_type* (*ipset_type_get_addr)(struct ipset_session *session, enum ipset_cmd cmd); int (*ipset_data_set_addr)(struct ipset_data *data, enum ipset_opt opt, const void *value); int (*ipset_cmd_addr)(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno); void (*ipset_load_types_addr)(void); /* We can (almost) make it look as though normal linking is being used */ #define ipset_session_init (*ipset_session_init_addr) #define ipset_session_fini (*ipset_session_fini_addr) #define ipset_session_data (*ipset_session_data_addr) #ifdef LIBIPSET_PRE_V7_COMPAT #define ipset_envopt_parse (*ipset_envopt_parse_addr) #else #define ipset_envopt_set (*ipset_envopt_set_addr) #endif #define ipset_type_get (*ipset_type_get_addr) #define ipset_data_set (*ipset_data_set_addr) /* Unfortunately ipset_cmd conflicts with struct ipset_cmd */ #define ipset_cmd1 (*ipset_cmd_addr) #define ipset_load_types (*ipset_load_types_addr) static void* libipset_handle; #else #define ipset_cmd1 ipset_cmd #endif static int #ifdef LIBIPSET_PRE_V7_COMPAT __attribute__ ((format(printf, 1, 2))) ipset_printf(const char *fmt, ...) #else __attribute__ ((format(printf, 3, 4))) ipset_printf(__attribute ((__unused__)) struct ipset_session *session, void *p, const char *fmt, ...) #endif { va_list args; #ifndef LIBIPSET_PRE_V7_COMPAT log_message(LOG_INFO, "libipset message from %s", PTR_CAST_CONST(char, p)); #endif va_start(args, fmt); vlog_message(LOG_INFO, fmt, args); va_end(args); return 0; } static bool do_ipset_cmd(struct ipset_session* session, enum ipset_cmd cmd, const char *setname, const ip_address_t *addr, int cidr, uint32_t timeout, const char* iface) { const struct ipset_type *type; uint8_t family; int r; ipset_session_data_set(session, IPSET_SETNAME, setname); type = ipset_type_get(session, cmd); if (type == NULL) { /* possible reasons for failure: set name does not exist */ return false; } family = (addr->ifa.ifa_family == AF_INET) ? NFPROTO_IPV4 : NFPROTO_IPV6; ipset_session_data_set(session, IPSET_OPT_FAMILY, &family); ipset_session_data_set(session, IPSET_OPT_IP, &addr->u); if (cidr >= 0) ipset_session_data_set(session, IPSET_OPT_CIDR, &cidr); if (timeout) ipset_session_data_set(session, IPSET_OPT_TIMEOUT, &timeout); if (iface) ipset_session_data_set(session, IPSET_OPT_IFACE, iface); r = ipset_cmd1(session, cmd, 0); return r == 0; } static bool ipset_create(struct ipset_session* session, const char *setname, const char *typename, uint8_t family) { const struct ipset_type *type; int r; ipset_session_data_set(session, IPSET_SETNAME, setname); ipset_session_data_set(session, IPSET_OPT_TYPENAME, typename); type = ipset_type_get(session, IPSET_CMD_CREATE); if (type == NULL) return false; ipset_session_data_set(session, IPSET_OPT_TYPE, type); ipset_session_data_set(session, IPSET_OPT_FAMILY, &family); r = ipset_cmd1(session, IPSET_CMD_CREATE, 0); return r == 0; } static bool ipset_destroy(struct ipset_session* session, const char *setname) { int r; ipset_session_data_set(session, IPSET_SETNAME, setname); r = ipset_cmd1(session, IPSET_CMD_DESTROY, 0); return r == 0; } static bool has_ipset_setname(struct ipset_session* session, const char *setname) { ipset_session_data_set(session, IPSET_SETNAME, setname); return ipset_cmd1(session, IPSET_CMD_HEADER, 0) == 0; } static bool create_sets(struct ipset_session **session, const char* addr4, const char* addr6, const char* addr_if6, const char *igmp, const char *mld, #ifndef _HAVE_VRRP_VMAC_ __attribute__((unused)) #endif const char *vmac_nd, bool is_reload) { if (!*session) #ifdef LIBIPSET_PRE_V7_COMPAT *session = ipset_session_init(ipset_printf); #else *session = ipset_session_init(ipset_printf, no_const(char, "create_sets")); #endif if (!*session) { log_message(LOG_INFO, "Cannot initialize ipset session."); return false; } /* If we aren't reloading, don't worry if sets already exists. With the * IPSET_ENV_EXIST option set, any existing entries in the set are removed. */ if (!is_reload) #ifdef LIBIPSET_PRE_V7_COMPAT ipset_envopt_parse(*session, IPSET_ENV_EXIST, NULL); #else ipset_envopt_set(*session, IPSET_ENV_EXIST); #endif if (addr4) { if (!is_reload || !has_ipset_setname(*session, addr4)) ipset_create(*session, addr4, "hash:ip", NFPROTO_IPV4); } if (addr6) { if (!is_reload || !has_ipset_setname(*session, addr6)) ipset_create(*session, addr6, "hash:ip", NFPROTO_IPV6); if (!is_reload || !has_ipset_setname(*session, addr_if6)) ipset_create(*session, addr_if6, "hash:net,iface", NFPROTO_IPV6); } if (igmp) { if (!is_reload || !has_ipset_setname(*session, igmp)) ipset_create(*session, igmp, "hash:net,iface", NFPROTO_IPV4); } if (mld) { if (!is_reload || !has_ipset_setname(*session, mld)) ipset_create(*session, mld, "hash:net,iface", NFPROTO_IPV6); } #ifdef _HAVE_VRRP_VMAC_ if (vmac_nd) { if (!is_reload || !has_ipset_setname(*session, vmac_nd)) ipset_create(*session, vmac_nd, "hash:net,iface", NFPROTO_IPV6); } #endif return true; } static bool set_match_loaded(void) { char buf[XT_FUNCTION_MAXNAMELEN+1]; FILE *fp; bool found = false; fp = fopen( "/proc/net/ip_tables_matches", "r"); if (!fp) return false; while (fgets(buf, sizeof(buf), fp)) { if ((buf[3] == '\0' || buf[3] == '\n') && !strncmp(buf, "set", 3)) { found = true; break; } } fclose(fp); return found; } bool ipset_initialise(void) { #ifdef _LIBIPSET_DYNAMIC_ if (libipset_handle) return true; #endif /* Don't attempt to use ipsets if running in a namespace and the default * set names have not been overridden and the kernel version is less * than Linux 3.13, since ipsets didn't understand namespaces prior to that. */ if (global_data->network_namespace && !global_data->namespace_with_ipsets && !strcmp(global_data->vrrp_ipset_address, DEFAULT_IPSET_NAME) && (os_major <= 2 || (os_major == 3 && os_minor < 13))) { log_message(LOG_INFO, "Not using ipsets with network namespace since not supported with kernel version < 3.13"); return false; } #ifdef _LIBIPSET_DYNAMIC_ /* Attempt to open the ipset library */ if (!(libipset_handle = dlopen("libipset.so", RTLD_NOW)) && !(libipset_handle = dlopen(IPSET_LIB_NAME, RTLD_NOW))) { log_message(LOG_INFO, "Unable to load ipset library - %s", dlerror()); return false; } if (!(ipset_session_init_addr = dlsym(libipset_handle, "ipset_session_init")) || !(ipset_session_fini_addr = dlsym(libipset_handle, "ipset_session_fini")) || !(ipset_session_data_addr = dlsym(libipset_handle,"ipset_session_data")) || #ifdef LIBIPSET_PRE_V7_COMPAT !(ipset_envopt_parse_addr = dlsym(libipset_handle,"ipset_envopt_parse")) || #else !(ipset_envopt_set_addr = dlsym(libipset_handle,"ipset_envopt_set")) || #endif !(ipset_type_get_addr = dlsym(libipset_handle,"ipset_type_get")) || !(ipset_data_set_addr = dlsym(libipset_handle,"ipset_data_set")) || !(ipset_cmd_addr = dlsym(libipset_handle,"ipset_cmd")) || !(ipset_load_types_addr = dlsym(libipset_handle,"ipset_load_types"))) { log_message(LOG_INFO, "Failed to dynamic link an ipset function - %s", dlerror()); return false; } #endif ipset_load_types(); if (!set_match_loaded() && !keepalived_modprobe("xt_set")) { log_message(LOG_INFO, "Unable to load module xt_set - not using ipsets"); return false; } return true; } // TODO - just revert to single call to remove_ipsets static bool remove_ipsets(struct ipset_session **session, uint8_t family, bool vip_sets) { if (!global_data->using_ipsets) return true; #ifdef _LIBIPSET_DYNAMIC_ if (!libipset_handle) return true; #endif if (!*session) #ifdef LIBIPSET_PRE_V7_COMPAT *session = ipset_session_init(ipset_printf); #else *session = ipset_session_init(ipset_printf, no_const(char, "remove_ipsets")); #endif if (!*session) { log_message(LOG_INFO, "Cannot initialize ipset session."); return false; } if (vip_sets) { if (family == AF_INET) ipset_destroy(*session, global_data->vrrp_ipset_address); else { ipset_destroy(*session, global_data->vrrp_ipset_address6); ipset_destroy(*session, global_data->vrrp_ipset_address_iface6); } } else { if (family == AF_INET) ipset_destroy(*session, global_data->vrrp_ipset_igmp); else { ipset_destroy(*session, global_data->vrrp_ipset_mld); #ifdef _HAVE_VRRP_VMAC_ ipset_destroy(*session, global_data->vrrp_ipset_vmac_nd); #endif } } return true; } bool remove_vip_ipsets(struct ipset_session **session, uint8_t family) { return remove_ipsets(session, family, true); } bool remove_igmp_ipsets(struct ipset_session **session, uint8_t family) { return remove_ipsets(session, family, false); } bool add_vip_ipsets(struct ipset_session **session, uint8_t family, bool is_reload) { if (family == AF_INET) return create_sets(session, global_data->vrrp_ipset_address, NULL, NULL, NULL, NULL, NULL, is_reload); return create_sets(session, NULL, global_data->vrrp_ipset_address6, global_data->vrrp_ipset_address_iface6, NULL, NULL, NULL, is_reload); } bool add_igmp_ipsets(struct ipset_session **session, uint8_t family, bool is_reload) { if (family == AF_INET) return create_sets(session, NULL, NULL, NULL, global_data->vrrp_ipset_igmp, NULL, NULL, is_reload); return create_sets(session, NULL, NULL, NULL, NULL, global_data->vrrp_ipset_mld, NULL, is_reload) #ifdef _HAVE_VRRP_VMAC_ && create_sets(session, NULL, NULL, NULL, NULL, NULL, global_data->vrrp_ipset_vmac_nd, is_reload) #endif ; } void* ipset_session_start(void) { #ifdef LIBIPSET_PRE_V7_COMPAT return ipset_session_init(ipset_printf); #else return ipset_session_init(ipset_printf, no_const(char, "session_start")); #endif } void ipset_session_end(void* vsession) { struct ipset_session *session = vsession; ipset_session_fini(session); } void ipset_entry(void* vsession, int cmd, const ip_address_t* addr) { const char* set; char *iface = NULL; struct ipset_session *session = vsession; if (addr->ifa.ifa_family == AF_INET) set = global_data->vrrp_ipset_address; else if (IN6_IS_ADDR_LINKLOCAL(&addr->u.sin6_addr)) { set = global_data->vrrp_ipset_address_iface6; iface = addr->ifp->ifname; } else set = global_data->vrrp_ipset_address6; do_ipset_cmd(session, (cmd == IPADDRESS_DEL) ? IPSET_CMD_DEL : IPSET_CMD_ADD, set, addr, -1, 0, iface); } void ipset_entry_igmp(void* vsession, int cmd, const char* ifname, uint8_t family) { const char* set; struct ipset_session *session = vsession; ip_address_t addr = { .ifa.ifa_family = AF_INET }; if (family == AF_INET) { set = global_data->vrrp_ipset_igmp; } else { set = global_data->vrrp_ipset_mld; addr.ifa.ifa_family = AF_INET6; } do_ipset_cmd(session, (cmd == IPADDRESS_DEL) ? IPSET_CMD_DEL : IPSET_CMD_ADD, set, &addr, 0, 0, ifname); } #ifdef _HAVE_VRRP_VMAC_ void ipset_entry_nd(void* vsession, int cmd, const interface_t* ifp) { struct ipset_session *session = vsession; ip_address_t addr = { .ifa.ifa_family = AF_INET6, .u.sin6_addr = ifp->base_ifp->sin6_addr }; do_ipset_cmd(session, (cmd == IPADDRESS_DEL) ? IPSET_CMD_DEL : IPSET_CMD_ADD, global_data->vrrp_ipset_vmac_nd, &addr, -1, 0, ifp->ifname); } #endif void set_default_ipsets(void) { global_data->vrrp_ipset_address = STRDUP(DEFAULT_IPSET_NAME); global_data->vrrp_ipset_address6 = STRDUP(DEFAULT_IPSET_NAME "6"); global_data->vrrp_ipset_address_iface6 = STRDUP(DEFAULT_IPSET_NAME "_if6"); global_data->vrrp_ipset_igmp = STRDUP(DEFAULT_IPSET_NAME "_igmp"); global_data->vrrp_ipset_mld = STRDUP(DEFAULT_IPSET_NAME "_mld"); #ifdef _HAVE_VRRP_VMAC_ global_data->vrrp_ipset_vmac_nd = STRDUP(DEFAULT_IPSET_NAME "_nd"); #endif } void disable_ipsets(void) { global_data->using_ipsets = false; FREE_CONST_PTR(global_data->vrrp_ipset_address); FREE_CONST_PTR(global_data->vrrp_ipset_address6); FREE_CONST_PTR(global_data->vrrp_ipset_address_iface6); FREE_CONST_PTR(global_data->vrrp_ipset_igmp); FREE_CONST_PTR(global_data->vrrp_ipset_mld); #ifdef _HAVE_VRRP_VMAC_ FREE_CONST_PTR(global_data->vrrp_ipset_vmac_nd); #endif } keepalived-2.3.3/keepalived/vrrp/vrrp_if_config.c0000664000175000017500000003746214512523637015601 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_if_config interface * * Author: Alexandre Cassen, * * 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, */ /* The following parameters need to be set on the vmac interface and its parent: * * vmac interface: * accept_local=1 // We need to be able to hear another instance multicasting it's presence * arp_ignore=1 // We mustn't reply to ARP requests on this interface for IP address on parent interface * // and we mustn't only reply to addresses on the same subnet. * rp_filter=0 // Allows us to receive on VMAC interface when it has no IP address. * * parent interface: * arp_ignore=1 // We mustn't reply to ARP requests on this interface for vrrp IP address * arp_filter=1 // We mustn't reply to ARP requests for our own IP address */ #include "config.h" #include #include "vrrp_if_config.h" #include "keepalived_netlink.h" #include "memory.h" #ifdef _HAVE_IPV4_DEVCONF_ #include #include #include "vrrp_if.h" #endif #include #include #include "logger.h" #ifdef _HAVE_VRRP_VMAC_ static unsigned all_rp_filter = UINT_MAX; static unsigned default_rp_filter = UINT_MAX; #endif #ifdef _HAVE_IPV4_DEVCONF_ typedef struct sysctl_opts { uint32_t param; uint32_t value; } sysctl_opts_t; #ifdef _HAVE_VRRP_VMAC_ static sysctl_opts_t parent_sysctl[] = { { IPV4_DEVCONF_ARP_IGNORE, 1 }, { IPV4_DEVCONF_ARPFILTER, 1 }, { 0, 0 } }; static sysctl_opts_t vmac_sysctl[] = { { IPV4_DEVCONF_ARP_IGNORE, 1 }, { IPV4_DEVCONF_ACCEPT_LOCAL, 1 }, { IPV4_DEVCONF_RP_FILTER, 0 }, { IPV4_DEVCONF_PROMOTE_SECONDARIES, 1 }, { 0, 0} }; static sysctl_opts_t vmac_sysctl_6[] = { { IPV4_DEVCONF_ARP_IGNORE, 1 }, { 0, 0} }; #endif #endif /* Sysctl get and set functions */ static void make_sysctl_filename(char *dest, const char* prefix, const char* iface, const char* parameter) { strcpy(dest, "/proc/sys/"); strcat(dest, prefix); strcat(dest, "/"); strcat(dest, iface); strcat(dest, "/"); strcat(dest, parameter); } #if !defined _HAVE_IPV4_DEVCONF_ || defined _HAVE_VRRP_VMAC_ static int set_sysctl(const char* prefix, const char* iface, const char* parameter, unsigned value) { char* filename; char buf[1]; int fd; ssize_t len; /* Make the filename */ filename = MALLOC(PATH_MAX); make_sysctl_filename(filename, prefix, iface, parameter); fd = open(filename, O_WRONLY); FREE(filename); if (fd < 0) return -1; /* We only write integers 0-9 */ buf[0] = (char)('0' + value); len = write(fd, &buf, 1); close(fd); if (len != 1) return -1; /* Success */ return 0; } #endif static unsigned get_sysctl(const char* prefix, const char* iface, const char* parameter) { char *filename; char buf[1]; int fd; ssize_t len; /* Make the filename */ filename = MALLOC(PATH_MAX); make_sysctl_filename(filename, prefix, iface, parameter); fd = open(filename, O_RDONLY); FREE(filename); if (fd < 0) return UINT_MAX; len = read(fd, &buf, 1); close(fd); /* We only read integers 0-9 */ if (len <= 0 || buf[0] < '0' || buf[0] > '9') return UINT_MAX; /* Return the value of the string read */ return (unsigned)buf[0] - '0'; } #ifdef _HAVE_IPV4_DEVCONF_ static struct nlattr * nest_start(struct nlmsghdr *nlh, unsigned short type) { struct nlattr *nest = PTR_CAST(struct nlattr, NLMSG_TAIL(nlh)); nest->nla_type = type; nlh->nlmsg_len += sizeof(struct nlattr); return nest; } static size_t nest_end(struct nlattr *nla, struct nlattr *nest) { nest->nla_len = (unsigned short)((char *)nla - (char *)nest); return nest->nla_len; } static inline int netlink_set_interface_flags(unsigned ifindex, const sysctl_opts_t *sys_opts) { int status = 0; struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[64]; } req; struct nlattr *start; struct nlattr *inet_start; struct nlattr *conf_start; const sysctl_opts_t *so; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_UNSPEC; req.ifi.ifi_index = ifindex; start = nest_start(&req.n, IFLA_AF_SPEC); inet_start = nest_start(&req.n, AF_INET); conf_start = nest_start(&req.n, IFLA_INET_CONF); for (so = sys_opts; so->param; so++) addattr32(&req.n, sizeof req, so->param, so->value); nest_end(PTR_CAST(struct nlattr, NLMSG_TAIL(&req.n)), conf_start); nest_end(PTR_CAST(struct nlattr, NLMSG_TAIL(&req.n)), inet_start); nest_end(PTR_CAST(struct nlattr, NLMSG_TAIL(&req.n)), start); if (netlink_talk(&nl_cmd, &req.n) < 0) status = 1; return status; } #ifdef _HAVE_VRRP_VMAC_ static inline int netlink_set_interface_parameters(const interface_t *ifp, interface_t *base_ifp, sa_family_t family) { if (netlink_set_interface_flags(ifp->ifindex, family == AF_INET6 ? vmac_sysctl_6 : vmac_sysctl)) return -1; if (family == AF_INET6) return 0; /* If the underlying interface is a MACVLAN that has been moved into * a separate network namespace from the parent, we can't access the * parent. */ if (IS_MAC_IP_VLAN(ifp) && ifp == base_ifp) return 0; /* Set arp_ignore and arp_filter on base interface if needed */ if (base_ifp->reset_arp_config) base_ifp->reset_arp_config++; else { if (base_ifp->arp_ignore != 1 || base_ifp->arp_filter != 1) { /* We can't use libnl3 since if the base interface type is a bridge, libnl3 sets ifi_family * to AF_BRIDGE, whereas it should be set to AF_UNSPEC. The kernel function that handles * RTM_SETLINK messages for AF_BRIDGE doesn't know how to process the IFLA_AF_SPEC attribute. */ if (netlink_set_interface_flags(base_ifp->ifindex, parent_sysctl)) { log_message(LOG_INFO, "Set base flags on %s failed for VMAC %s", base_ifp->ifname, ifp->ifname); return -1; } base_ifp->reset_arp_config = 1; } } return 0; } static inline int netlink_reset_interface_parameters(const interface_t* ifp) { int res; sysctl_opts_t reset_parent_sysctl[3]; /* If the interface doesn't exist, there is nothing we can change */ if (!ifp->ifindex) return 0; /* See netlink3_set_interface_parameters for why libnl3 can't be used */ reset_parent_sysctl[0].param = IPV4_DEVCONF_ARP_IGNORE; reset_parent_sysctl[0].value = ifp->arp_ignore; reset_parent_sysctl[1].param = IPV4_DEVCONF_ARPFILTER; reset_parent_sysctl[1].value = ifp->arp_filter; reset_parent_sysctl[2].param = 0; if ((res = netlink_set_interface_flags(ifp->ifindex, reset_parent_sysctl))) log_message(LOG_INFO, "reset interface flags on %s failed", ifp->ifname); return res; } static inline void set_interface_parameters_devconf(const interface_t *ifp, interface_t *base_ifp, sa_family_t family) { if (netlink_set_interface_parameters(ifp, base_ifp, family)) log_message(LOG_INFO, "Unable to set parameters for %s", ifp->ifname); } static inline void reset_interface_parameters_devconf(interface_t *base_ifp) { if (base_ifp->reset_arp_config && --base_ifp->reset_arp_config == 0) { if (netlink_reset_interface_parameters(base_ifp)) log_message(LOG_INFO, "Unable to reset parameters for %s", base_ifp->ifname); } } #endif static inline void set_promote_secondaries_devconf(interface_t *ifp) { sysctl_opts_t promote_secondaries_sysctl[] = { { IPV4_DEVCONF_PROMOTE_SECONDARIES, 1 }, { 0, 0} }; if (ifp->promote_secondaries) return; netlink_set_interface_flags(ifp->ifindex, promote_secondaries_sysctl); } static inline void reset_promote_secondaries_devconf(interface_t *ifp) { sysctl_opts_t promote_secondaries_sysctl[] = { { IPV4_DEVCONF_PROMOTE_SECONDARIES, 0 }, { 0, 0} }; netlink_set_interface_flags(ifp->ifindex, promote_secondaries_sysctl); } #else #ifdef _HAVE_VRRP_VMAC_ static inline void set_interface_parameters_sysctl(const interface_t *ifp, interface_t *base_ifp, sa_family_t family) { unsigned val; set_sysctl("net/ipv4/conf", ifp->ifname, "arp_ignore", 1); if (family == AF_INET6) return; set_sysctl("net/ipv4/conf", ifp->ifname, "accept_local", 1); set_sysctl("net/ipv4/conf", ifp->ifname, "rp_filter", 0); set_sysctl("net/ipv4/conf", ifp->ifname, "promote_secondaries", 1); /* If the underlying interface is a MACVLAN that has been moved into * a separate network namespace from the parent, we can't access the * parent. */ if (IS_MAC_IP_VLAN(ifp) && ifp == base_ifp) return; if (base_ifp->reset_arp_config) base_ifp->reset_arp_config++; else { if ((val = get_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_ignore")) != UINT_MAX && (base_ifp->arp_ignore = (uint32_t)val) != 1) set_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_ignore", 1); if ((val = get_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_filter")) != UINT_MAX && (base_ifp->arp_filter = (uint32_t)val) != 1) set_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_filter", 1); base_ifp->reset_arp_config = 1; } } static inline void reset_interface_parameters_sysctl(interface_t *base_ifp) { if (base_ifp->reset_arp_config && --base_ifp->reset_arp_config == 0) { set_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_ignore", (int)base_ifp->arp_ignore); set_sysctl("net/ipv4/conf", base_ifp->ifname, "arp_filter", (int)base_ifp->arp_filter); } } #endif static inline void set_promote_secondaries_sysctl(interface_t *ifp) { if (get_sysctl("net/ipv4/conf", ifp->ifname, "promote_secondaries") == 1) { ifp->promote_secondaries = true; return; } set_sysctl("net/ipv4/conf", ifp->ifname, "promote_secondaries", 1); } static inline void reset_promote_secondaries_sysctl(interface_t *ifp) { set_sysctl("net/ipv4/conf", ifp->ifname, "promote_secondaries", 0); } #endif void set_promote_secondaries(interface_t *ifp) { if (ifp->promote_secondaries) return; if (ifp->reset_promote_secondaries++) return; #ifdef _HAVE_IPV4_DEVCONF_ set_promote_secondaries_devconf(ifp); #else set_promote_secondaries_sysctl(ifp); #endif } void reset_promote_secondaries(interface_t *ifp) { if (!ifp->reset_promote_secondaries || --ifp->reset_promote_secondaries) return; #ifdef _HAVE_IPV4_DEVCONF_ reset_promote_secondaries_devconf(ifp); #else reset_promote_secondaries_sysctl(ifp); #endif } #ifdef _HAVE_VRRP_VMAC_ /* IPv4 VMAC interfaces require rp_filter to be 0; this in turn requires * net.ipv4.conf.all.rp_filter to be 0, but if it is non-zero, then all * interfaces will be operating with a non-zero value of rp_filter. * In this function, if all.rp_filter > 0 and default.rp_filter < all.rp_filter, * we first set default.rp_filter to the current value of all.rp_filter, * so that any new interfaces are created with the current value of all.rp_filter. * We then iterate through all interfaces, and if {interface}.rp_filter < all.rp_filter * we set {interface}.rp_filter = all.rp_filter. * Finally we set all.rp_filter = 0. * * This should not alter the operation of any interface, or any interface * subsequently created, but it does allow us to set rp_filter = 0 * on vmac interfaces. */ static void clear_rp_filter(void) { list_head_t *ifq; interface_t *ifp; unsigned rp_filter; #ifdef _HAVE_IPV4_DEVCONF_ sysctl_opts_t rpfilter_sysctl[] = { { IPV4_DEVCONF_RP_FILTER, 1 }, { 0, 0} }; #endif rp_filter = get_sysctl("net/ipv4/conf", "all", "rp_filter"); if (rp_filter == UINT_MAX) { log_message(LOG_INFO, "Unable to read sysctl net.ipv4.conf.all.rp_filter"); return; } if (rp_filter == 0) return; /* Save current value of all/rp_filter */ all_rp_filter = rp_filter; /* We want to ensure that default/rp_filter is at least the value of all/rp_filter */ rp_filter = get_sysctl("net/ipv4/conf", "default", "rp_filter"); if (rp_filter < all_rp_filter) { log_message(LOG_INFO, "NOTICE: setting sysctl net.ipv4.conf.default.rp_filter from %u to %u", rp_filter, all_rp_filter); set_sysctl("net/ipv4/conf", "default", "rp_filter", all_rp_filter); default_rp_filter = rp_filter; } /* Now ensure rp_filter for all interfaces is at least all/rp_filter. */ #ifdef _HAVE_IPV4_DEVCONF_ rpfilter_sysctl[0].value = all_rp_filter; #endif kernel_netlink_poll(); /* Update our view of interfaces first */ ifq = get_interface_queue(); list_for_each_entry(ifp, ifq, e_list) { if (!ifp->ifindex) continue; #ifndef _HAVE_IPV4_DEVCONF_ if ((ifp->rp_filter = get_sysctl("net/ipv4/conf", ifp->ifname, "rp_filter")) == UINT_MAX) log_message(LOG_INFO, "Unable to read rp_filter for %s", ifp->ifname); else #endif if (ifp->rp_filter < all_rp_filter) { #ifdef _HAVE_IPV4_DEVCONF_ netlink_set_interface_flags(ifp->ifindex, rpfilter_sysctl); #else set_sysctl("net/ipv4/conf", ifp->ifname, "rp_filter", all_rp_filter); #endif } else { /* Indicate we are not setting it */ ifp->rp_filter = UINT_MAX; } } /* We have now made sure that all the interfaces have rp_filter >= all_rp_filter */ log_message(LOG_INFO, "NOTICE: setting sysctl net.ipv4.conf.all.rp_filter from %u to 0", all_rp_filter); set_sysctl("net/ipv4/conf", "all", "rp_filter", 0); } void restore_rp_filter(void) { list_head_t *ifq; interface_t *ifp; unsigned rp_filter; #ifdef _HAVE_IPV4_DEVCONF_ sysctl_opts_t rpfilter_sysctl[] = { { IPV4_DEVCONF_RP_FILTER, 1 }, { 0, 0} }; #endif /* Restore the original settings of rp_filter, but only if they * are the same as what we set them to */ if (all_rp_filter == UINT_MAX) return; rp_filter = get_sysctl("net/ipv4/conf", "all", "rp_filter"); if (rp_filter == 0) { log_message(LOG_INFO, "NOTICE: resetting sysctl net.ipv4.conf.all.rp_filter to %u", all_rp_filter); set_sysctl("net/ipv4/conf", "all", "rp_filter", all_rp_filter); } if (default_rp_filter != UINT_MAX) { rp_filter = get_sysctl("net/ipv4/conf", "default", "rp_filter"); if (rp_filter == all_rp_filter) { log_message(LOG_INFO, "NOTICE: resetting sysctl net.ipv4.conf.default.rp_filter to %u", default_rp_filter); set_sysctl("net/ipv4/conf", "default", "rp_filter", default_rp_filter); } default_rp_filter = UINT_MAX; } ifq = get_interface_queue(); list_for_each_entry(ifp, ifq, e_list) { if (ifp->rp_filter != UINT_MAX) { rp_filter = get_sysctl("net/ipv4/conf", ifp->ifname, "rp_filter"); if (rp_filter == all_rp_filter) { #ifdef _HAVE_IPV4_DEVCONF_ rpfilter_sysctl[0].value = ifp->rp_filter; netlink_set_interface_flags(ifp->ifindex, rpfilter_sysctl); #else set_sysctl("net/ipv4/conf", ifp->ifname, "rp_filter", ifp->rp_filter); #endif } } } all_rp_filter = UINT_MAX; } void set_interface_parameters(const interface_t *ifp, interface_t *base_ifp, sa_family_t family) { if (all_rp_filter == UINT_MAX) clear_rp_filter(); #ifdef _HAVE_IPV4_DEVCONF_ set_interface_parameters_devconf(ifp, base_ifp, family); #else set_interface_parameters_sysctl(ifp, base_ifp, family); #endif } void reset_interface_parameters(interface_t *base_ifp) { #ifdef _HAVE_IPV4_DEVCONF_ reset_interface_parameters_devconf(base_ifp); #else reset_interface_parameters_sysctl(base_ifp); #endif } void link_set_ipv6(const interface_t* ifp, bool enable) { /* There is no direct way to set IPv6 options */ set_sysctl("net/ipv6/conf", ifp->ifname, "disable_ipv6", enable ? 0 : 1); } #endif void set_ipv6_forwarding(interface_t* ifp) { ifp->gna_router = !!get_sysctl("net/ipv6/conf", ifp->ifname, "forwarding"); ifp->last_gna_router_check = time_now; } keepalived-2.3.3/keepalived/vrrp/vrrp_if.c0000664000175000017500000013724614771471130014252 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Interfaces manipulation. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" /* global include */ #include #include #include #include #include #include #include #include #include #include #include #if defined _HAVE_NETINET_LINUX_IF_ETHER_H_COLLISION_ && \ defined _LINUX_IF_ETHER_H && \ !defined _NETINET_IF_ETHER_H /* musl libc throws an error if is included before , * so we stop being included if has been included. */ #define _NETINET_IF_ETHER_H #endif #include /* needed to get correct values for SIOC* */ #include #include #include #include /* local include */ #include "global_data.h" #include "vrrp.h" #include "vrrp_if.h" #include "vrrp_daemon.h" #include "keepalived_netlink.h" #include "utils.h" #include "logger.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_vmac.h" #include "bitops.h" #endif #include "track_file.h" #include "vrrp_track.h" #include "vrrp_scheduler.h" #include "vrrp_iproute.h" #ifdef THREAD_DUMP #include "scheduler.h" #endif #ifdef _WITH_FIREWALL_ #include "vrrp_firewall.h" #endif /* Local vars */ static LIST_HEAD_INITIALIZE(if_queue); #ifdef _WITH_LINKBEAT_ static struct ifreq ifr; static int linkbeat_fd = -1; #endif static LIST_HEAD_INITIALIZE(old_garp_delay); /* Global vars */ LIST_HEAD_INITIALIZE(garp_delay); /* Helper functions */ interface_t * __attribute__ ((pure)) if_get_by_ifindex(ifindex_t ifindex) { interface_t *ifp; if (!ifindex) return NULL; list_for_each_entry(ifp, &if_queue, e_list) { if (ifp->ifindex == ifindex) return ifp; } return NULL; } #ifdef _HAVE_VRRP_VMAC_ interface_t * __attribute__ ((pure)) if_get_by_vmac(uint8_t vrid, int family, const interface_t *base_ifp, const u_char hw_addr[ETH_ALEN]) { interface_t *ifp; list_for_each_entry(ifp, &if_queue, e_list) { if (ifp->if_type != IF_TYPE_MACVLAN || ifp->vmac_type != MACVLAN_MODE_PRIVATE) continue; if (ifp->base_ifp != base_ifp) continue; if (hw_addr) { if (memcmp(ifp->hw_addr, hw_addr, ETH_ALEN)) continue; } else { if (ifp->hw_addr[0] || ifp->hw_addr[1] || ifp->hw_addr[2] != 0x5e || ifp->hw_addr[3]) continue; if ((family == AF_INET && ifp->hw_addr[4] != 0x01) || (family == AF_INET6 && ifp->hw_addr[4] != 0x02)) continue; if (ifp->hw_addr[5] != vrid) continue; } ifp->is_ours = true; return ifp; } return NULL; } #endif interface_t * get_default_if(void) { const char *ifname = global_data->default_ifname ? global_data->default_ifname : DFLT_INT; if (!global_data->default_ifp) global_data->default_ifp = if_get_by_ifname(ifname, IF_CREATE_IF_DYNAMIC); return global_data->default_ifp; } sin_addr_t * if_extra_ipaddress_alloc(interface_t *ifp, void *addr, unsigned char family) { sin_addr_t *saddr; PMALLOC(saddr); INIT_LIST_HEAD(&saddr->e_list); if (family == AF_INET) { saddr->u.sin_addr = *PTR_CAST(struct in_addr, addr); list_add_tail(&saddr->e_list, &ifp->sin_addr_l); return saddr; } if (family == AF_INET6) { saddr->u.sin6_addr = *PTR_CAST(struct in6_addr, addr); list_add_tail(&saddr->e_list, &ifp->sin6_addr_l); return saddr; } FREE(saddr); return NULL; } void if_extra_ipaddress_free(sin_addr_t *addr) { list_del_init(&addr->e_list); FREE(addr); } void if_extra_ipaddress_free_list(list_head_t *l) { sin_addr_t *addr, *addr_tmp; list_for_each_entry_safe(addr, addr_tmp, l, e_list) if_extra_ipaddress_free(addr); } static void if_tracking_vrrp_dump_list(FILE *fp, const list_head_t *l) { tracking_obj_t *top; list_for_each_entry(top, l, e_list) dump_tracking_vrrp(fp, top); } interface_t * if_get_by_ifname(const char *ifname, if_lookup_t create) { interface_t *ifp; list_for_each_entry(ifp, &if_queue, e_list) { if (!strcmp(ifp->ifname, ifname)) return create == IF_CREATE_NOT_EXIST ? NULL : ifp; } if (create == IF_NO_CREATE || (create == IF_CREATE_IF_DYNAMIC && (!global_data || !global_data->dynamic_interfaces))) { if (create == IF_CREATE_IF_DYNAMIC) non_existent_interface_specified = true; return NULL; } if (!(ifp = MALLOC(sizeof(interface_t)))) return NULL; strcpy_safe(ifp->ifname, ifname); #ifdef _HAVE_VRRP_VMAC_ ifp->base_ifp = ifp; ifp->if_type = IF_TYPE_STANDARD; #endif INIT_LIST_HEAD(&ifp->sin_addr_l); INIT_LIST_HEAD(&ifp->sin6_addr_l); INIT_LIST_HEAD(&ifp->tracking_vrrp); INIT_LIST_HEAD(&ifp->e_list); list_add_tail(&ifp->e_list, &if_queue); if (create == IF_CREATE_IF_DYNAMIC) log_message(LOG_INFO, "Configuration specifies interface %s which doesn't currently exist - will use if created", ifname); return ifp; } #ifdef _HAVE_VRRP_VMAC_ /* Set the base_ifp for VMACs and IPVLANs and vrf_master_ifp for VRFs - only used at startup */ static void set_base_ifp(void) { interface_t *ifp; #ifdef _HAVE_VRF_ interface_t *master_ifp; #endif list_for_each_entry(ifp, &if_queue, e_list) { if ((!ifp->base_ifp || ifp == ifp->base_ifp) && ifp->base_ifindex) { #ifdef HAVE_IFLA_LINK_NETNSID if (ifp->base_netns_id != -1) ifp->base_ifp = NULL; else #endif ifp->base_ifp = if_get_by_ifindex(ifp->base_ifindex); /* If this is a MACVLAN/IPVLAN that has been moved into a separate network namespace * from its parent, then we can't get information about the parent. */ if (!ifp->base_ifp) ifp->base_ifp = ifp; else ifp->base_ifindex = 0; /* This is only used at startup, so ensure not used later */ } #ifdef _HAVE_VRF_ /* Now see if the interface is enslaved to a VRF */ if (ifp->vrf_master_ifindex) { master_ifp = if_get_by_ifindex(ifp->vrf_master_ifindex); if (master_ifp && master_ifp->vrf_master_ifp == master_ifp) ifp->vrf_master_ifp = master_ifp; ifp->vrf_master_ifindex = 0; } #endif } } #endif #ifdef _WITH_LINKBEAT_ /* MII Transceiver Registers poller functions */ static uint16_t if_mii_read(int fd, uint16_t phy_id, uint16_t reg_num) { struct mii_ioctl_data *data = PTR_CAST(struct mii_ioctl_data, &ifr.ifr_data); data->phy_id = phy_id; data->reg_num = reg_num; if (ioctl(fd, SIOCGMIIREG, &ifr) < 0) { log_message(LOG_ERR, "SIOCGMIIREG on %s failed: %s", ifr.ifr_name, strerror(errno)); return 0xffff; } return data->val_out; } #ifdef _INCLUDE_UNUSED_CODE_ static void if_mii_dump(const uint16_t *mii_regs, size_t num_regs, unsigned phy_id) { int mii_reg; printf(" MII PHY #%d transceiver registers:", phy_id); for (mii_reg = 0; mii_reg < num_regs; mii_reg++) printf("%s %4.4x", (mii_reg % 8) == 0 ? "\n ":"", mii_regs[mii_reg]); printf("\n"); } #endif static int if_mii_status(const int fd) { struct mii_ioctl_data *data = PTR_CAST(struct mii_ioctl_data, &ifr.ifr_data); uint16_t phy_id = data->phy_id; uint16_t bmsr, new_bmsr; if (if_mii_read(fd, phy_id, MII_BMCR) == 0xffff || (bmsr = if_mii_read(fd, phy_id, MII_BMSR)) == 0) { log_message(LOG_ERR, "No MII transceiver present for %s !!!", ifr.ifr_name); return -1; } // if_mii_dump(mii_regs, sizeof(mii_regs)/ sizeof(mii_regs[0], phy_id); /* * For Basic Mode Status Register (BMSR). * Sticky field (Link established & Jabber detected), we need to read * a second time the BMSR to get current status. */ new_bmsr = if_mii_read(fd, phy_id, MII_BMSR); // log_message(LOG_INFO, " \nBasic Mode Status Register 0x%4.4x ... 0x%4.4x\n", bmsr, new_bmsr); if (bmsr & BMSR_LSTATUS || new_bmsr & BMSR_LSTATUS) return LINK_UP; return LINK_DOWN; } static int if_mii_probe(const int fd, const char *ifname) { struct mii_ioctl_data *data = PTR_CAST(struct mii_ioctl_data, &ifr.ifr_data); uint16_t phy_id; memset(&ifr, 0, sizeof (struct ifreq)); strcpy_safe(ifr.ifr_name, ifname); if (ioctl(fd, SIOCGMIIPHY, &ifr) < 0) return -1; /* check if the driver reports BMSR using the MII interface, as we * will need this and we already know that some don't support it. */ phy_id = data->phy_id; /* save it in case it is overwritten */ data->reg_num = MII_BMSR; if (ioctl(fd, SIOCGMIIREG, &ifr) < 0) return -1; data->phy_id = phy_id; /* Dump the MII transceiver */ return if_mii_status(fd); } static inline int if_ethtool_status(const int fd) { struct ethtool_value edata; edata.cmd = ETHTOOL_GLINK; ifr.ifr_data = (caddr_t) & edata; if (ioctl(fd, SIOCETHTOOL, &ifr)) return -1; return (edata.data) ? LINK_UP : LINK_DOWN; } static int if_ethtool_probe(const int fd, const interface_t *ifp) { int status; memset(&ifr, 0, sizeof (struct ifreq)); strcpy_safe(ifr.ifr_name, ifp->ifname); status = if_ethtool_status(fd); return status; } /* Returns false if interface is down */ static bool if_ioctl_flags(const int fd, interface_t *ifp) { memset(&ifr, 0, sizeof (struct ifreq)); strcpy(ifr.ifr_name, ifp->ifname); if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) return (errno != ENODEV) ? LINK_UP : LINK_DOWN; return FLAGS_UP(ifr.ifr_flags) ? LINK_UP : LINK_DOWN; } #endif /* garp_delay facility function */ void free_garp_delay(garp_delay_t *gd) { list_del_init(&gd->e_list); FREE(gd); } static void free_garp_delay_list(list_head_t *l) { garp_delay_t *gd, *gd_tmp; list_for_each_entry_safe(gd, gd_tmp, l, e_list) free_garp_delay(gd); } static void dump_garp_delay(FILE *fp, const garp_delay_t *gd) { char time_str[26]; interface_t *ifp; conf_write(fp, "------< GARP delay group >------"); if (gd->have_garp_interval) { conf_write(fp, " GARP interval = %" PRI_tv_sec ".%6.6" PRI_tv_usec, gd->garp_interval.tv_sec, gd->garp_interval.tv_usec); if (!ctime_r(&gd->garp_next_time.tv_sec, time_str)) strcpy(time_str, "invalid time "); conf_write(fp, " GARP next time %" PRI_tv_sec ".%6.6" PRI_tv_usec " (%.19s.%6.6" PRI_tv_usec ")", gd->garp_next_time.tv_sec, gd->garp_next_time.tv_usec, time_str, gd->garp_next_time.tv_usec); } if (gd->have_gna_interval) { conf_write(fp, " GNA interval = %" PRI_tv_sec ".%6.6" PRI_tv_usec, gd->gna_interval.tv_sec, gd->gna_interval.tv_usec); if (!ctime_r(&gd->gna_next_time.tv_sec, time_str)) strcpy(time_str, "invalid time "); conf_write(fp, " GNA next time %" PRI_tv_sec ".%6.6" PRI_tv_usec " (%.19s.%6.6" PRI_tv_usec ")", gd->gna_next_time.tv_sec, gd->gna_next_time.tv_usec, time_str, gd->gna_next_time.tv_usec); } else if (!gd->have_garp_interval) conf_write(fp, " No configuration"); conf_write(fp, " Interfaces"); list_for_each_entry(ifp, &if_queue, e_list) { if (ifp->garp_delay == gd) conf_write(fp, " %s", ifp->ifname); } } void dump_garp_delay_list(FILE *fp, list_head_t *l) { garp_delay_t *gd; list_for_each_entry(gd, l, e_list) dump_garp_delay(fp, gd); } garp_delay_t * alloc_garp_delay(void) { garp_delay_t *gd; PMALLOC(gd); INIT_LIST_HEAD(&gd->e_list); INIT_LIST_HEAD(&gd->garp_list); INIT_LIST_HEAD(&gd->gna_list); list_add_tail(&gd->e_list, &garp_delay); return gd; } static void set_garp_delay(interface_t *ifp, const garp_delay_t *delay) { ifp->garp_delay = alloc_garp_delay(); ifp->garp_delay->garp_interval = delay->garp_interval; ifp->garp_delay->have_garp_interval = delay->have_garp_interval; ifp->garp_delay->gna_interval = delay->gna_interval; ifp->garp_delay->have_gna_interval = delay->have_gna_interval; } void set_default_garp_delay(void) { garp_delay_t default_delay = {0}; interface_t *ifp; vrrp_t *vrrp; list_head_t *vip_list; ip_address_t *vip; bool have_ipv4, have_ipv6; if (global_data->vrrp_garp_interval) { default_delay.garp_interval.tv_sec = global_data->vrrp_garp_interval / TIMER_HZ; default_delay.garp_interval.tv_usec = global_data->vrrp_garp_interval % TIMER_HZ; default_delay.have_garp_interval = true; } if (global_data->vrrp_gna_interval) { default_delay.gna_interval.tv_sec = global_data->vrrp_gna_interval / TIMER_HZ; default_delay.gna_interval.tv_usec = global_data->vrrp_gna_interval % TIMER_HZ; default_delay.have_gna_interval = true; } /* Allocate a delay structure to each physical interface that doesn't have one and * is being used by a VRRP instance */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!vrrp->ifp) continue; /* Check what family of addresses we have */ have_ipv4 = vrrp->family == AF_INET; have_ipv6 = vrrp->family == AF_INET6; list_for_each_entry(vip, &vrrp->evip, e_list) { if (IP_IS6(vip)) have_ipv6 = true; else have_ipv4 = true; } /* We don't need a delay if there isn't a delay for the * address family we are using */ if (!((have_ipv4 && global_data->vrrp_garp_interval) || (have_ipv6 && global_data->vrrp_gna_interval))) continue; ifp = IF_BASE_IFP(vrrp->ifp); if (!ifp->garp_delay) set_garp_delay(ifp, &default_delay); /* We also need delays for any i/fs used by VIPs/eVIPs */ for (vip_list = &vrrp->vip; vip_list; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL) { list_for_each_entry(vip, vip_list, e_list) { ifp = IF_BASE_IFP(vip->ifp); if (!ifp->garp_delay) set_garp_delay(ifp, &default_delay); } } } } static void free_if(interface_t *ifp) { free_tracking_obj_list(&ifp->tracking_vrrp); if_extra_ipaddress_free_list(&ifp->sin_addr_l); if_extra_ipaddress_free_list(&ifp->sin6_addr_l); FREE(ifp); } static void dump_if(FILE *fp, const interface_t *ifp) { char addr_str[INET6_ADDRSTRLEN]; char mac_buf[3 * sizeof ifp->hw_addr]; sin_addr_t *saddr; char time_str[26]; conf_write(fp, " Name = %s", ifp->ifname); conf_write(fp, " index = %u%s", ifp->ifindex, ifp->ifindex ? "" : " (deleted)"); if (!ifp->ifindex) { /* This duplicates code below, but it is simpler, and clearer, * than having lost of "if (ifp->ifindex)" tests */ #ifdef _HAVE_VRRP_VMAC_ if (ifp->is_ours) conf_write(fp, " I/f created by keepalived"); #endif if (!list_empty(&ifp->tracking_vrrp)) { conf_write(fp, " Tracking VRRP instances :"); if_tracking_vrrp_dump_list(fp, &ifp->tracking_vrrp); } return; } conf_write(fp, " IPv4 address = %s", ifp->sin_addr.s_addr ? inet_ntop2(ifp->sin_addr.s_addr) : "(none)"); if (!list_empty(&ifp->sin_addr_l)) { conf_write(fp, " Additional IPv4 addresses :"); list_for_each_entry(saddr, &ifp->sin_addr_l, e_list) conf_write(fp, " %s", inet_ntop2(saddr->u.sin_addr.s_addr)); } if (!IN6_IS_ADDR_UNSPECIFIED(&ifp->sin6_addr)) { inet_ntop(AF_INET6, &ifp->sin6_addr, addr_str, sizeof(addr_str)); conf_write(fp, " IPv6 address = %s", addr_str); } else conf_write(fp, " IPv6 address = (none)"); if (!list_empty(&ifp->sin6_addr_l)) { conf_write(fp, " Additional IPv6 addresses :"); list_for_each_entry(saddr, &ifp->sin6_addr_l, e_list) { inet_ntop(AF_INET6, &saddr->u.sin6_addr, addr_str, sizeof(addr_str)); conf_write(fp, " %s", addr_str); } } if (ifp->hw_addr_len) { format_mac_buf(mac_buf, sizeof mac_buf, ifp->hw_addr, ifp->hw_addr_len); conf_write(fp, " MAC = %s", mac_buf); format_mac_buf(mac_buf, sizeof mac_buf, ifp->hw_addr_bcast, ifp->hw_addr_len); conf_write(fp, " MAC broadcast = %s", mac_buf); } conf_write(fp, " State = %sUP, %sRUNNING%s%s%s%s%s%s", ifp->ifi_flags & IFF_UP ? "" : "not ", ifp->ifi_flags & IFF_RUNNING ? "" : "not ", !(ifp->ifi_flags & IFF_BROADCAST) ? ", no broadcast" : "", ifp->ifi_flags & IFF_LOOPBACK ? ", loopback" : "", ifp->ifi_flags & IFF_POINTOPOINT ? ", point to point" : "", ifp->ifi_flags & IFF_NOARP ? ", no arp" : "", !(ifp->ifi_flags & IFF_MULTICAST) ? ", no multicast" : "", #ifdef _HAVE_VRRP_VMAC_ ifp != ifp->base_ifp && !(ifp->base_ifp->ifi_flags & IFF_UP) ? ", master down" : "" #else "" #endif ); conf_write(fp, " Seen up = %d", ifp->seen_up); conf_write(fp, " Delayed state change running = %s", ifp->flags_change_thread ? "true" : "false"); conf_write(fp, " Up debounce timer = %uus", ifp->up_debounce_timer); conf_write(fp, " Down debounce timer = %uus", ifp->down_debounce_timer); #ifdef _HAVE_VRRP_VMAC_ conf_write(fp, " Group = %u", ifp->group); if (IS_MAC_IP_VLAN(ifp)) { const char *if_type = #ifdef _HAVE_VRRP_IPVLAN_ ifp->if_type == IF_TYPE_IPVLAN ? "IPVLAN" : #endif "VMAC"; const char *vlan_type = ifp->if_type == IF_TYPE_MACVLAN ? ifp->vmac_type == MACVLAN_MODE_PRIVATE ? "private" : ifp->vmac_type == MACVLAN_MODE_VEPA ? "vepa" : ifp->vmac_type == MACVLAN_MODE_BRIDGE ? "bridge" : #ifdef MACVLAN_MODE_PASSTHRU ifp->vmac_type == MACVLAN_MODE_PASSTHRU ? "passthru" : #endif #ifdef MACVLAN_MODE_SOURCE ifp->vmac_type == MACVLAN_MODE_SOURCE ? "source" : #endif "unknown" : #if defined _HAVE_VRRP_IPVLAN_ && HAVE_DECL_IFLA_IPVLAN_FLAGS ifp->ipvlan_flags & IPVLAN_F_PRIVATE ? "private" : ifp->ipvlan_flags & IPVLAN_F_VEPA ? "vepa" : #endif "bridge"; const char *ipvlan_mode = #ifdef _HAVE_VRRP_IPVLAN_ ifp->if_type == IF_TYPE_IPVLAN ? (ifp->vmac_type == IPVLAN_MODE_L2 ? "L2 " : ifp->vmac_type == IPVLAN_MODE_L3 ? "L3 " : #if HAVE_DECL_IPVLAN_MODE_L3S ifp->vmac_type == IPVLAN_MODE_L3S ? "L3S " : #endif "unknown mode ") : ""; #else ""; #endif if (ifp != ifp->base_ifp) conf_write(fp, " %s type %s%s, underlying interface = %s, state = %sUP, %sRUNNING", if_type, ipvlan_mode, vlan_type, ifp->base_ifp->ifname, ifp->base_ifp->ifi_flags & IFF_UP ? "" : "not ", ifp->base_ifp->ifi_flags & IFF_RUNNING ? "" : "not "); else if (ifp->base_ifindex) { #ifdef HAVE_IFLA_LINK_NETNSID conf_write(fp, " %s type %s, underlying ifindex = %u, netns id = %d", if_type, vlan_type, ifp->base_ifindex, ifp->base_netns_id); #else conf_write(fp, " %s type %s, underlying ifindex = %u", if_type, vlan_type, ifp->base_ifindex); #endif } else conf_write(fp, " %s type %s, underlying interface in different namespace", if_type, vlan_type); } if (ifp->is_ours) conf_write(fp, " I/f created by keepalived"); else if (global_data->allow_if_changes && ifp->changeable_type) conf_write(fp, " Interface type/base can be changed"); if (ifp->seen_interface) conf_write(fp, " Done VRID check"); #endif conf_write(fp, " MTU = %" PRIu32, ifp->mtu); switch (ifp->hw_type) { case ARPHRD_LOOPBACK: conf_write(fp, " HW Type = LOOPBACK"); break; case ARPHRD_ETHER: conf_write(fp, " HW Type = ETHERNET"); break; case ARPHRD_INFINIBAND: log_message(LOG_INFO, " HW Type = INFINIBAND"); break; default: conf_write(fp, " HW Type = UNKNOWN (%d)", ifp->hw_type); break; } #ifdef _WITH_LINKBEAT_ if (!ifp->linkbeat_use_polling) conf_write(fp, " NIC netlink status update"); else if (IF_MII_SUPPORTED(ifp)) conf_write(fp, " NIC support MII regs"); else if (IF_ETHTOOL_SUPPORTED(ifp)) conf_write(fp, " NIC support ETHTOOL GLINK interface"); else conf_write(fp, " NIC ioctl refresh polling"); #endif #ifdef _HAVE_VRF_ if (ifp->vrf_master_ifp == ifp) conf_write(fp, " VRF master table %u", ifp->vrf_tb_id); else if (ifp->vrf_master_ifp) conf_write(fp, " VRF slave of %s", ifp->vrf_master_ifp->ifname); #endif if (ifp->garp_delay) { if (ifp->garp_delay->have_garp_interval) conf_write(fp, " Gratuitous ARP interval %" PRI_tv_sec "ms", ifp->garp_delay->garp_interval.tv_sec * 1000 + ifp->garp_delay->garp_interval.tv_usec / (TIMER_HZ / 1000)); if (ifp->garp_delay->have_gna_interval) conf_write(fp, " Gratuitous NA interval %" PRI_time_t "ms", ifp->garp_delay->gna_interval.tv_sec * 1000 + ifp->garp_delay->gna_interval.tv_usec / (TIMER_HZ / 1000)); } #ifdef _HAVE_VRRP_VMAC_ conf_write(fp, " Reset ARP config counter %d", ifp->reset_arp_config); conf_write(fp, " Original arp_ignore %d", ifp->arp_ignore); conf_write(fp, " Original arp_filter %d", ifp->arp_filter); if (ifp->rp_filter < UINT_MAX) conf_write(fp, " rp_filter %u", ifp->rp_filter); #endif conf_write(fp, " Original promote_secondaries %d", ifp->promote_secondaries); conf_write(fp, " Reset promote_secondaries counter %" PRIu32, ifp->reset_promote_secondaries); if (timerisset(&ifp->last_gna_router_check)) { ctime_r(&ifp->last_gna_router_check.tv_sec, time_str); conf_write(fp, " %sIPv6 forwarding. Last checked %" PRI_tv_sec ".%6.6" PRI_tv_usec " (%.24s.%6.6" PRI_tv_usec ")", ifp->gna_router ? "" : "Not ", ifp->last_gna_router_check.tv_sec, ifp->last_gna_router_check.tv_usec, time_str, ifp->last_gna_router_check.tv_usec); } if (!list_empty(&ifp->tracking_vrrp)) { conf_write(fp, " Tracking VRRP instances :"); if_tracking_vrrp_dump_list(fp, &ifp->tracking_vrrp); } } #ifdef _WITH_LINKBEAT_ static bool init_linkbeat_status(int fd, interface_t *ifp) { int status; bool if_up = false; int configured_type = ifp->lb_type; if ((!ifp->lb_type || ifp->lb_type == LB_MII)) { if ((status = if_mii_probe(fd, ifp->ifname)) >= 0) { ifp->lb_type = LB_MII; if_up = !!status; } else ifp->lb_type = 0; } if ((!ifp->lb_type || ifp->lb_type == LB_ETHTOOL)) { if ((status = if_ethtool_probe(fd, ifp)) >= 0) { ifp->lb_type = LB_ETHTOOL; if_up = !!status; } else { /* If ETHTOOL was configured on i/f but doesn't work, try MII */ if (ifp->lb_type && (status = if_mii_probe(fd, ifp->ifname)) >= 0) { ifp->lb_type = LB_MII; if_up = !!status; } else ifp->lb_type = 0; } } if ((!ifp->lb_type || ifp->lb_type == LB_IOCTL)) { ifp->lb_type = LB_IOCTL; if_up = true; } if (if_up) if_up = if_ioctl_flags(fd, ifp); if (configured_type && configured_type != ifp->lb_type) log_message(LOG_INFO, "(%s): Configured linkbeat type %s not supported, using %s", ifp->ifname, configured_type == LB_MII ? "MII" : configured_type == LB_ETHTOOL ? "ETHTOOL" : "IOCTL", ifp->lb_type == LB_MII ? "MII" : ifp->lb_type == LB_ETHTOOL ? "ETHTOOL" : "IOCTL"); return if_up; } static void if_linkbeat_refresh_thread(thread_ref_t thread) { interface_t *ifp = THREAD_ARG(thread); bool if_up = true, was_up; was_up = IF_FLAGS_UP(ifp); if (!ifp->ifindex) { if_up = false; } else { if (!ifp->lb_type) { /* If this is a new interface, we need to find linkbeat type */ if_up = init_linkbeat_status(linkbeat_fd, ifp); } else { if (IF_MII_SUPPORTED(ifp)) if_up = if_mii_probe(linkbeat_fd, ifp->ifname); else if (IF_ETHTOOL_SUPPORTED(ifp)) if_up = if_ethtool_probe(linkbeat_fd, ifp); /* * update ifp->flags to get the new IFF_RUNNING status. * Some buggy drivers need this... */ if (if_up) if_up = if_ioctl_flags(linkbeat_fd, ifp); } } if (if_up) ifp->ifi_flags |= IFF_UP | IFF_RUNNING; else ifp->ifi_flags &= ~(IFF_UP | IFF_RUNNING); if (if_up != was_up) { log_message(LOG_INFO, "Linkbeat reports %s %s", ifp->ifname, if_up ? "up" : "down"); process_if_status_change(ifp); } /* Register next polling thread */ thread_add_timer(master, if_linkbeat_refresh_thread, ifp, POLLING_DELAY); } void init_interface_linkbeat(void) { interface_t *ifp; bool linkbeat_in_use = false; bool if_up; list_for_each_entry(ifp, &if_queue, e_list) { if (!ifp->linkbeat_use_polling) continue; /* Don't poll an interface that we aren't using */ if (list_empty(&ifp->tracking_vrrp)) { log_message(LOG_INFO, "Turning off linkbeat for %s since not used for tracking", ifp->ifname); ifp->linkbeat_use_polling = false; ifp->lb_type = 0; continue; } #ifdef _HAVE_VRRP_VMAC_ /* netlink messages work for vmacs */ if (IS_MAC_IP_VLAN(ifp)) { log_message(LOG_INFO, "Turning off linkbeat for %s since netlink works for vmacs/ipvlans", ifp->ifname); ifp->linkbeat_use_polling = false; continue; } #endif if (linkbeat_fd == -1) { if ((linkbeat_fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) { log_message(LOG_INFO, "open linkbeat init socket failed - errno %d - %m\n", errno); return; } } linkbeat_in_use = true; if (!ifp->ifindex) { /* Interface doesn't exist yet */ ifp->ifi_flags = 0; } else { if_up = init_linkbeat_status(linkbeat_fd, ifp); if (if_up) ifp->ifi_flags |= IFF_UP | IFF_RUNNING; else ifp->ifi_flags &= ~(IFF_UP | IFF_RUNNING); } /* Register new monitor thread */ thread_add_timer(master, if_linkbeat_refresh_thread, ifp, POLLING_DELAY); } if (linkbeat_in_use) log_message(LOG_INFO, "Using MII-BMSR/ETHTOOL NIC polling thread(s)..."); } void close_interface_linkbeat(void) { if (linkbeat_fd != -1) { close(linkbeat_fd); linkbeat_fd = -1; } } #endif /* Interface queue helpers*/ void free_interface_queue(void) { interface_t *ifp, *ifp_tmp; list_for_each_entry_safe(ifp, ifp_tmp, &if_queue, e_list) free_if(ifp); free_garp_delay_list(&garp_delay); } void free_old_interface_queue(void) { free_garp_delay_list(&old_garp_delay); } void dump_interface_queue(FILE *fp, list_head_t *l) { interface_t *ifp; list_for_each_entry(ifp, l, e_list) dump_if(fp, ifp); } list_head_t * get_interface_queue(void) { return &if_queue; } void reset_interface_queue(void) { interface_t *ifp; list_copy(&old_garp_delay, &garp_delay); INIT_LIST_HEAD(&garp_delay); list_for_each_entry(ifp, &if_queue, e_list) { #ifdef _WITH_LINKBEAT_ ifp->linkbeat_use_polling = false; #endif ifp->garp_delay = NULL; free_tracking_obj_list(&ifp->tracking_vrrp); } } void init_interface_queue(void) { netlink_interface_lookup(NULL); #ifdef _HAVE_VRRP_VMAC_ /* Since we are reading all the interfaces, we might have received details of * a vmac/vrf before the underlying interface, so now we need to ensure the * interface pointers are all set */ set_base_ifp(); #endif // dump_interface_queue(NULL, &if_queue); } int if_join_vrrp_group(sa_family_t family, int *sd, const interface_t *ifp, const sockaddr_t* mcast_daddr) { struct ip_mreqn imr; struct ipv6_mreq imr6; int ret = 0; #if defined _HAVE_VRRP_VMAC_ bool send_on_base_if; #endif if (*sd < 0) return -1; /* -> outbound processing option * join the multicast group. * binding the socket to the interface for outbound multicast * traffic. */ /* We don't really want to send the IGMP/MLD messages on a VMAC * interface, since that will send using the 00:00:5e:00:0x:xx mac * address, and snooping switches will then be updated, even if we * are backup. * We have to join the group on the VMAC interface, otherwise we cannot * receive the messages to the multicast address (if we try receiving * on the base interface we don't see the messages since there is an * interface on the system with the MAC address matching the source address * of the packet). If we are using nftables and the dup statement is supported, * we just let nftables move the IGMP join message to the physical interface, * otherwise we need to join on both the VMAC interface and the physical * interface, and use nftables/iptables to drop the packet on the VMAC * interface. * If we are using neither nftables or iptables, there is no point in * duplicating the join, since we can't block it on the VMAC interface. * * This might all be better achieved using eBPF. */ #if defined _HAVE_VRRP_VMAC_ send_on_base_if = false; if (IS_MAC_IP_VLAN(ifp) && ifp->if_type == IF_TYPE_MACVLAN && ifp->is_ours) { #ifdef _WITH_IPTABLES_ if (global_data->vrrp_iptables_outchain) send_on_base_if = true; #endif #ifdef _WITH_NFTABLES_ if (global_data->vrrp_nf_table_name) send_on_base_if = !HAVE_DECL_NFTA_DUP_MAX; #endif } #endif if (family == AF_INET) { memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr = PTR_CAST_CONST(struct sockaddr_in, mcast_daddr)->sin_addr; /* -> Need to handle multicast convergance after takeover. * We retry until multicast is available on the interface. */ #if defined _HAVE_VRRP_VMAC_ /* coverity[dead_error_condition] */ if (send_on_base_if) { imr.imr_ifindex = IF_INDEX(IF_BASE_IFP(ifp)); if (setsockopt(*sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, PTR_CAST(char, &imr), (socklen_t)sizeof(struct ip_mreqn)) < 0) log_message(LOG_INFO, "Failed to set GARP on base if - errno %d (%m)", errno); } #endif imr.imr_ifindex = (int)IF_INDEX(ifp); ret = setsockopt(*sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, PTR_CAST(char, &imr), (socklen_t)sizeof(struct ip_mreqn)); } else { memset(&imr6, 0, sizeof(imr6)); imr6.ipv6mr_multiaddr = PTR_CAST_CONST(struct sockaddr_in6, mcast_daddr)->sin6_addr; #if defined _HAVE_VRRP_VMAC_ /* coverity[dead_error_condition] */ if (send_on_base_if) { imr6.ipv6mr_interface = IF_INDEX(IF_BASE_IFP(ifp)); if (setsockopt(*sd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, PTR_CAST(char, &imr6), (socklen_t)sizeof(struct ipv6_mreq)) < 0) log_message(LOG_INFO, "Failed to set MLD on base if - errno %d (%m)", errno); } #endif imr6.ipv6mr_interface = IF_INDEX(ifp); ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, PTR_CAST(char, &imr6), (socklen_t)sizeof(struct ipv6_mreq)); } if (ret < 0) { log_message(LOG_INFO, "(%s) cant do IP%s_ADD_MEMBERSHIP %s errno=%s (%d)", ifp->ifname, (family == AF_INET) ? "" : "v6", inet_sockaddrtos(mcast_daddr), strerror(errno), errno); close(*sd); *sd = -1; } return *sd; } #ifdef _INCLUDE_UNUSED_CODE_ int if_leave_vrrp_group(sa_family_t family, int sd, const interface_t *ifp, const sockaddr_t *mcast_addr) { struct ip_mreqn imr; struct ipv6_mreq imr6; int ret = 0; /* If fd is -1 then we add a membership trouble */ if (sd < 0 || !ifp) return -1; /* Leaving the VRRP multicast group */ if (family == AF_INET) { memset(&imr, 0, sizeof(imr)); imr.imr_multiaddr = mcast_daddr; #if defined _HAVE_VRRP_VMAC_ && defined _WITH_NFTABLES_ && !HAVE_DECL_NFTA_DUP_MAX /* See description in if_join_vrrp_group */ if (IS_MAC_IP_VLAN(ifp) && ifp->if_type == IF_TYPE_MACVLAN && ifp->is_ours) { imr.imr_ifindex = IF_INDEX(IF_BASE_IFP(ifp)); setsockopt(sd, IPPROTO_IP, IP_DROP_MEMBERSHIP, PTR_CAST(char, &imr), sizeof(imr)); } imr.imr_ifindex = (int)IF_INDEX(ifp); ret = setsockopt(sd, IPPROTO_IP, IP_DROP_MEMBERSHIP, PTR_CAST(char, &imr), sizeof(imr)); #endif } else { memset(&imr6, 0, sizeof(imr6)); imr6.ipv6mr_multiaddr = mcast_daddr; #if defined _HAVE_VRRP_VMAC_ && defined _WITH_NFTABLES_ && !HAVE_DECL_NFTA_DUP_MAX /* See description in if_join_vrrp_group */ if (IS_MAC_IP_VLAN(ifp) && ifp->if_type == IF_TYPE_MACVLAN && ifp->is_ours) { imr6.ipv6mr_interface = IF_INDEX(IF_BASE_IFP(ifp)); setsockopt(sd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, PTR_CAST(char, &imr6), sizeof(struct ipv6_mreq)); } #endif imr6.ipv6mr_interface = IF_INDEX(ifp); ret = setsockopt(sd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, PTR_CAST(char, &imr6), sizeof(struct ipv6_mreq)); } if (ret < 0) { /* coverity[deadcode] */ log_message(LOG_INFO, "(%s) cant do IP%s_DROP_MEMBERSHIP %s errno=%s (%d)", ifp->ifname, (family == AF_INET) ? "" : "v6", inet_sockaddrtos(mcast_daddr), strerror(errno), errno); return -1; } return 0; } #endif int if_setsockopt_bindtodevice(int *sd, const interface_t *ifp) { int ret; if (*sd < 0) return -1; /* -> inbound processing option * Specify the bound_dev_if. * why IP_ADD_MEMBERSHIP & IP_MULTICAST_IF doesnt set * sk->bound_dev_if themself ??? !!! * Needed for filter multicasted advert per interface. * * -- If you read this !!! and know the answer to the question * please feel free to answer me ! :) */ ret = setsockopt(*sd, SOL_SOCKET, SO_BINDTODEVICE, IF_NAME(ifp), (socklen_t)strlen(IF_NAME(ifp)) + 1); if (ret < 0) { log_message(LOG_INFO, "can't bind to device %s. errno=%d. (try to run it as root)", IF_NAME(ifp), errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_hdrincl(int *sd) { int ret; int on = 1; if (*sd < 0) return -1; /* Include IP header into RAW protocol packet */ ret = setsockopt(*sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)); if (ret < 0) { log_message(LOG_INFO, "cant set HDRINCL IP option. errno=%d (%m)", errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_ipv6_checksum(int *sd) { int ret; int offset = 6; if (!sd || *sd < 0) return -1; ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset)); if (ret < 0) { log_message(LOG_INFO, "cant set IPV6_CHECKSUM IP option. errno=%d (%m)", errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_mcast_all(sa_family_t family, int *sd) { int ret; int no = 0; if (*sd < 0) return -1; /* Don't accept multicast packets we haven't requested */ if (family == AF_INET) ret = setsockopt(*sd, IPPROTO_IP, IP_MULTICAST_ALL, &no, sizeof(no)); else { #if HAVE_DECL_IPV6_MULTICAST_ALL ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_MULTICAST_ALL, &no, sizeof(no)); #else return *sd; #endif } if (ret < 0) { log_message(LOG_INFO, "cant set IP%s_MULTICAST_ALL IP option. errno=%d (%m)", family == AF_INET ? "" : "V6", errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_mcast_loop(sa_family_t family, int *sd) { int ret; int loop = 0; if (*sd < 0) return -1; /* Set Multicast loop */ if (family == AF_INET) ret = setsockopt(*sd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); else ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)); if (ret < 0) { log_message(LOG_INFO, "cant set IP%s_MULTICAST_LOOP IP option. errno=%d (%m)", (family == AF_INET) ? "" : "V6", errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_mcast_hops(sa_family_t family, int *sd) { int ret; int hops = 255; /* Not applicable for IPv4 */ if (*sd < 0 || family == AF_INET) return -1; /* Set HOP limit */ ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); if (ret < 0) { log_message(LOG_INFO, "cant set IPV6_MULTICAST_HOPS IP option. errno=%d (%m)", errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_mcast_if(sa_family_t family, int *sd, const interface_t *ifp) { int ret; ifindex_t ifindex; int int_ifindex; if (*sd < 0) return -1; /* Set interface for sending outbound datagrams */ ifindex = IF_INDEX(ifp); if ( family == AF_INET) { struct ip_mreqn imr; memset(&imr, 0, sizeof(imr)); imr.imr_ifindex = (int)IF_INDEX(ifp); ret = setsockopt(*sd, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr)); } else { int_ifindex = (int)ifindex; ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &int_ifindex, sizeof(int_ifindex)); } if (ret < 0) { log_message(LOG_INFO, "cant set IP%s_MULTICAST_IF IP option. errno=%d (%m)", (family == AF_INET) ? "" : "V6", errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_priority(int *sd, int family) { int ret; int val; if (*sd < 0) return -1; /* Set PRIORITY for VRRP traffic */ if (family == AF_INET) { val = IPTOS_PREC_INTERNETCONTROL; ret = setsockopt(*sd, IPPROTO_IP, IP_TOS, &val, sizeof(val)); } else { /* set tos to internet network control */ val = 0xc0; /* 192, which translates to DCSP value 48, or cs6 */ ret = setsockopt(*sd, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)); } if (ret < 0) { log_message(LOG_INFO, "can't set %s option. errno=%d (%m)", (family == AF_INET) ? "IP_TOS" : "IPV6_TCLASS", errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_rcvbuf(int *sd, int val) { int ret; if (*sd < 0) return -1; /* rcvbuf option */ ret = setsockopt(*sd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)); if (ret < 0) { log_message(LOG_INFO, "cant set SO_RCVBUF IP option. errno=%d (%m)", errno); close(*sd); *sd = -1; } return *sd; } int if_setsockopt_no_receive(int *sd) { int ret; struct sock_filter bpfcode[1] = { BPF_STMT(BPF_RET | BPF_K, 0), /* ret #0 - means that all packets will be filtered out */ }; struct sock_fprog bpf = { sizeof(bpfcode) / sizeof(bpfcode[0]), bpfcode }; if (*sd < 0) return -1; ret = setsockopt(*sd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)); if (ret < 0) { log_message(LOG_INFO, "Can't set SO_ATTACH_FILTER option. errno=%d (%m)", errno); close(*sd); *sd = -1; } return *sd; } void interface_up(interface_t *ifp) { /* We need to re-add static addresses and static routes */ static_track_group_reinstate_config(ifp); } void interface_down(interface_t *ifp) { vrrp_t *vrrp; ip_route_t *route; bool route_found; /* Unfortunately the kernel doesn't send RTM_DELROUTE for userspace added * routes that are deleted when the link goes down (?kernel bug). */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->state != VRRP_STATE_MAST) continue; route_found = false; list_for_each_entry(route, &vrrp->vroutes, e_list) { if (!route->set) continue; /* Any route that has an oif will be tracking the interface, * so we only need to check for routes that dont specify an * oif */ /* Don't track route if it's not configured with this down * interface. */ if (!route->oif || route->configured_ifindex != ifp->ifindex) continue; route->set = false; if (route->dont_track) continue; route_found = true; } if (route_found) { /* Bring down vrrp instance/sync group */ #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && VRRP_CONFIGURED_IFP(vrrp) == ifp) down_instance(vrrp, VRRP_FAULT_FL_BASE_INTERFACE_DOWN); else #endif down_instance(vrrp, VRRP_FAULT_FL_INTERFACE_DOWN); } } /* Now check the static routes */ list_for_each_entry(route, &vrrp_data->static_routes, e_list) { if (route->set && route->oif == ifp) { /* This route will have been deleted */ route->set = false; } } } void cleanup_lost_interface(interface_t *ifp) { tracking_obj_t *top; vrrp_t *vrrp; list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; /* If this instance does not have an interface, we don't need to do anything, but I don't this can ever be true */ if (!vrrp->ifp) continue; if (vrrp->ifp != ifp #ifdef _HAVE_VRRP_VMAC_ && IF_BASE_IFP(vrrp->ifp) != ifp && VRRP_CONFIGURED_IFP(vrrp) != ifp #endif ) { /* We must be a tracked interface */ if (IF_ISUP(ifp)) { if (top->weight) { vrrp->total_priority -= top->weight * top->weight_multiplier; vrrp_set_effective_priority(vrrp); } else { #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && VRRP_CONFIGURED_IFP(vrrp) == ifp) down_instance(vrrp, VRRP_FAULT_FL_BASE_INTERFACE_DOWN); else #endif down_instance(vrrp, VRRP_FAULT_FL_INTERFACE_DOWN); } } continue; } /* If the vrrp instance's interface doesn't exist, skip it */ if (!vrrp->ifp->ifindex) continue; #ifdef _HAVE_VRRP_VMAC_ /* If vmac going, clear VMAC_UP_BIT on vrrp instance */ if (vrrp->ifp->is_ours) { __clear_bit(VRRP_VMAC_UP_BIT, &vrrp->flags); #ifdef _WITH_FIREWALL_ firewall_remove_vmac(vrrp); #endif } if (vrrp->configured_ifp == ifp && vrrp->configured_ifp->base_ifp != vrrp->configured_ifp) del_vrrp_from_interface(vrrp, vrrp->configured_ifp->base_ifp); /* If the interface type can be changed, and the vrrp had a * duplicate VRID, clear the error since when the underlying * interface is created again, it may be on another underlying * interface, and there may not be a duplicate VRID. */ if (global_data->allow_if_changes && ifp->changeable_type && vrrp->configured_ifp == ifp && __test_bit(VRRP_FLAG_DUPLICATE_VRID_FAULT, &vrrp->flags)) { __clear_bit(VRRP_FLAG_DUPLICATE_VRID_FAULT, &vrrp->flags); __clear_bit(VRRP_FAULT_FL_DUPLICATE_VRID, &vrrp->flags_if_fault); } #endif /* Find the sockpool entry. If none, then we have closed the socket */ if (vrrp->sockets) { if (vrrp->sockets->fd_in != -1) { thread_cancel_read(master, vrrp->sockets->fd_in); close(vrrp->sockets->fd_in); vrrp->sockets->fd_in = -1; } if (vrrp->sockets->fd_out != -1) { close(vrrp->sockets->fd_out); vrrp->sockets->fd_out = -1; } } if (IF_ISUP(ifp)) { #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && VRRP_CONFIGURED_IFP(vrrp) == ifp) down_instance(vrrp, VRRP_FAULT_FL_BASE_INTERFACE_DOWN); else #endif down_instance(vrrp, VRRP_FAULT_FL_INTERFACE_DOWN); } } interface_down(ifp); ifp->ifindex = 0; ifp->ifi_flags = 0; ifp->seen_up = false; #ifdef _HAVE_VRRP_VMAC_ if (!ifp->is_ours) ifp->base_ifp = ifp; #endif #ifdef _HAVE_VRF_ ifp->vrf_master_ifp = NULL; ifp->vrf_master_ifindex = 0; #endif #ifdef _HAVE_VRRP_VMAC_ list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (!vrrp->ifp) continue; if (vrrp->configured_ifp == ifp && vrrp->configured_ifp->base_ifp == vrrp->ifp->base_ifp && vrrp->ifp->is_ours) { /* This is a changeable interface that the vrrp instance * was configured on. Delete the macvlan/ipvlan we created */ netlink_link_del_vmac(vrrp); } } #endif } static void setup_interface(vrrp_t *vrrp) { vrrp_t *vrrp_l; #ifdef _HAVE_VRRP_VMAC_ /* If the vrrp instance uses a vmac, and that vmac i/f doesn't * exist, then create it */ if (!vrrp->ifp->ifindex) { /* coverity[var_deref_model] - vrrp->configured_ifp is not NULL for VMAC */ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && !netlink_link_add_vmac(vrrp, NULL)) return; #ifdef _HAVE_VRRP_IPVLAN_ /* coverity[var_deref_model] - vrrp->configured_ifp is not NULL for IPVLAN */ else if (__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) && !netlink_link_add_ipvlan(vrrp)) return; #endif } #endif /* Find the sockpool entry. If none, then we open the socket */ if (vrrp->sockets->fd_in == -1) { /* If the MTU has changed we may need to recalculate the socket receive buffer size */ if (global_data->vrrp_rx_bufs_policy & RX_BUFS_POLICY_MTU) { vrrp->sockets->rx_buf_size = 0; rb_for_each_entry(vrrp_l, &vrrp->sockets->rb_vrid, rb_vrid) { if (vrrp_l->kernel_rx_buf_size) vrrp->sockets->rx_buf_size += vrrp_l->kernel_rx_buf_size; else vrrp->sockets->rx_buf_size += global_data->vrrp_rx_bufs_multiples * vrrp_l->ifp->mtu; } } open_sockpool_socket(vrrp->sockets); if (vrrp_initialised) { vrrp->state = vrrp->flags_if_fault ? VRRP_STATE_FAULT : VRRP_STATE_BACK; vrrp_init_instance_sands(vrrp); vrrp_thread_add_read(vrrp); } } /* If we have created a VMAC for the vrrp instance, the VMAC has an address * (IPv6 only) and the vrrp instance does not have an address, add the * address to the vrrp instance and try to bring it up. */ if (vrrp->family == AF_INET6) { if (IN6_IS_ADDR_UNSPECIFIED(&vrrp->saddr) && (__test_bit(VRRP_FAULT_FL_NO_SOURCE_IP, &vrrp->flags_if_fault))) { inet_ip6tosockaddr(&vrrp->ifp->sin6_addr, &vrrp->saddr); try_up_instance(vrrp, false, VRRP_FAULT_FL_NO_SOURCE_IP); } } } #ifdef _HAVE_VRRP_VMAC_ void recreate_vmac_thread(thread_ref_t thread) { interface_t *ifp = THREAD_ARG(thread); tracking_obj_t *top; vrrp_t *vrrp; list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; /* If this isn't the vrrp's interface, skip */ if (vrrp->ifp != ifp) continue; if (!__test_bit(VRRP_VMAC_BIT, &vrrp->flags) #ifdef _HAVE_VRRP_IPVLAN_ && !__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) #endif ) continue; /* Don't attempt to create the VMAC if the configured * interface doesn't exist */ if (!VRRP_CONFIGURED_IFP(vrrp)->ifindex) continue; netlink_error_ignore = ENODEV; setup_interface(vrrp); netlink_error_ignore = 0; break; } } #endif void update_mtu(interface_t *ifp) { sock_t *sock; bool updated_vrrp_buffer = false; vrrp_t *vrrp; list_for_each_entry(sock, &vrrp_data->vrrp_socket_pool, e_list) { if (sock->ifp != ifp || sock->fd_in == -1) continue; if (!updated_vrrp_buffer) { alloc_vrrp_buffer(ifp->mtu); updated_vrrp_buffer = true; } /* If the MTU has changed we may need to recalculate the socket receive buffer size */ if (global_data->vrrp_rx_bufs_policy & RX_BUFS_POLICY_MTU) { sock->rx_buf_size = 0; rb_for_each_entry(vrrp, &sock->rb_vrid, rb_vrid) { if (vrrp->kernel_rx_buf_size) sock->rx_buf_size += vrrp->kernel_rx_buf_size; else sock->rx_buf_size += global_data->vrrp_rx_bufs_multiples * ifp->mtu; } if (setsockopt(sock->fd_in, SOL_SOCKET, SO_RCVBUF, &sock->rx_buf_size, sizeof(sock->rx_buf_size))) log_message(LOG_INFO, "vrrp update receive socket buffer size error %d", errno); } } } void update_added_interface(interface_t *ifp) { vrrp_t *vrrp; tracking_obj_t *top; #ifdef _HAVE_VRRP_VMAC_ vrrp_t *vrrp1; tracking_obj_t *top1; #endif list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; #ifdef _HAVE_VRRP_VMAC_ /* If this interface is a macvlan that we haven't created, * and the interface type can be changed or we haven't checked * this interface before, make sure that there is no VRID * conflict. */ if (!ifp->is_ours && (global_data->allow_if_changes || !ifp->seen_interface) && !list_empty(&ifp->base_ifp->tracking_vrrp)) { // TODO - handle unicast - see check_vrrp_conflicts() - in fact, can we use it? list_for_each_entry(top1, &ifp->base_ifp->tracking_vrrp, e_list) { vrrp1 = top1->obj.vrrp; if (vrrp == vrrp1) continue; if (!vrrp1->ifp) continue; if (!VRRP_CONFIGURED_IFP(vrrp1)->ifindex) continue; if (IF_BASE_IFP(VRRP_CONFIGURED_IFP(vrrp)) == IF_BASE_IFP(VRRP_CONFIGURED_IFP(vrrp1)) && vrrp->family == vrrp1->family && vrrp->vrid == vrrp1->vrid) { __set_bit(VRRP_FAULT_FL_DUPLICATE_VRID, &vrrp->flags_if_fault); __set_bit(VRRP_FLAG_DUPLICATE_VRID_FAULT, &vrrp->flags); log_message(LOG_INFO, "VRID conflict between %s and %s IPv%d vrid %d", vrrp->iname, vrrp1->iname, vrrp->family == AF_INET ? 4 : 6, vrrp->vrid); break; } } } if ( #ifdef _HAVE_VRRP_VMAC_ __test_bit(VRRP_VMAC_BIT, &vrrp->flags) #ifdef _HAVE_VRRP_IPVLAN_ || __test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) #endif #endif ) { if (top->type & TRACK_VRRP) { add_vrrp_to_interface(vrrp, ifp->base_ifp, top->weight, top->weight_multiplier == -1, false, TRACK_VRRP_DYNAMIC); if (!IF_ISUP(vrrp->configured_ifp->base_ifp) && !__test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags)) { log_message(LOG_INFO, "(%s) interface %s is down", vrrp->iname, vrrp->configured_ifp->base_ifp->ifname); __set_bit(VRRP_FAULT_FL_BASE_INTERFACE_DOWN, &vrrp->flags_if_fault); } } /* We might be the configured interface for a vrrp instance that itself uses * a macvlan. If so, we can create the macvlans */ if (vrrp->configured_ifp == ifp && !vrrp->ifp->ifindex) thread_add_event(master, recreate_vmac_thread, vrrp->ifp, 0); } #endif if (!vrrp->ifp) continue; /* If this is just a tracking interface, we don't need to do anything */ if (vrrp->ifp != ifp #ifdef _HAVE_VRRP_VMAC_ && IF_BASE_IFP(vrrp->ifp) != ifp #endif ) continue; /* Reopen any socket on this interface if necessary */ if ( #ifdef _HAVE_VRRP_VMAC_ !__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && #ifdef _HAVE_VRRP_IPVLAN_ !__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags) && #endif #endif vrrp->sockets->fd_in == -1) setup_interface(vrrp); } #ifdef _HAVE_VRRP_VMAC_ ifp->seen_interface = true; #endif } #ifdef THREAD_DUMP void register_vrrp_if_addresses(void) { #ifdef _WITH_LINKBEAT_ register_thread_address("if_linkbeat_refresh_thread", if_linkbeat_refresh_thread); #endif #ifdef _HAVE_VRRP_VMAC_ register_thread_address("recreate_vmac_thread", recreate_vmac_thread); #endif } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_static_track.c0000664000175000017500000001437714011452216016315 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Tracking static addresses/routes/rules framework. * * Author: Quentin Armitage, * * 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) 2018-2020 Alexandre Cassen, */ #include "config.h" /* local include */ #include "vrrp_track.h" #include "vrrp_data.h" #include "vrrp.h" #include "vrrp_sync.h" #include "logger.h" #include "vrrp_static_track.h" #include "vrrp_ipaddress.h" #include "vrrp_iproute.h" #include "vrrp_iprule.h" static void free_static_track_group_vrrp_list(list_head_t *l) { tracking_obj_t *top, *top_tmp; list_for_each_entry_safe(top, top_tmp, l, e_list) FREE(top); } void free_static_track_group(static_track_group_t *tgroup) { if (tgroup->iname) { /* If we are terminating at init time, tgroup->vrrp may not be initialised yet, in * which case tgroup->iname will still be set */ if (!list_empty(&tgroup->vrrp_instances)) log_message(LOG_INFO, "track group %s - iname vector exists when freeing group" , tgroup->gname); free_strvec(tgroup->iname); } list_del_init(&tgroup->e_list); FREE_CONST(tgroup->gname); free_static_track_group_vrrp_list(&tgroup->vrrp_instances); FREE(tgroup); } void dump_static_track_group(FILE *fp, const static_track_group_t *tgroup) { tracking_obj_t *top; conf_write(fp, " Static Track Group = %s", tgroup->gname); if (!list_empty(&tgroup->vrrp_instances)) { conf_write(fp, " VRRP member instances :"); list_for_each_entry(top, &tgroup->vrrp_instances, e_list) conf_write(fp, " %s", top->obj.vrrp->iname); } } static_track_group_t * __attribute__ ((pure)) static_track_group_find(const char *gname) { static_track_group_t *tgroup; list_for_each_entry(tgroup, &vrrp_data->static_track_groups, e_list) if (!strcmp(gname, tgroup->gname)) return tgroup; return NULL; } static bool static_track_group_set(static_track_group_t *tgroup) { tracking_obj_t *top; vrrp_t *vrrp; char *str; unsigned int i; /* Can't handle no members of the group */ if (!tgroup->iname) { log_message(LOG_INFO, "Static track group %s has no virtual router(s)" , tgroup->gname); return false; } for (i = 0; i < vector_size(tgroup->iname); i++) { str = vector_slot(tgroup->iname, i); vrrp = vrrp_get_instance(str); if (!vrrp) { log_message(LOG_INFO, "vrrp instance %s specified in track group %s doesn't exist - ignoring" , str, tgroup->gname); continue; } /* Create tracking object */ PMALLOC(top); INIT_LIST_HEAD(&top->e_list); top->obj.vrrp = vrrp; top->type = TRACK_VRRP; list_add_tail(&top->e_list, &tgroup->vrrp_instances); } /* The iname vector is only used for us to set up the sync groups, so delete it */ free_strvec(tgroup->iname); tgroup->iname = NULL; if (list_empty(&tgroup->vrrp_instances)) { log_message(LOG_INFO, "Static track group %s has no VRRP instance(s)" , tgroup->gname); return false; } return true; } void static_track_group_init(void) { static_track_group_t *tgroup, *tgroup_tmp; tracking_obj_t *top; ip_address_t *addr; ip_route_t *route; ip_rule_t *rule; list_for_each_entry_safe(tgroup, tgroup_tmp, &vrrp_data->static_track_groups, e_list) { if (!static_track_group_set(tgroup)) { log_message(LOG_INFO, "Static track group %s init fails - removing" , tgroup->gname); free_static_track_group(tgroup); } } /* Add the tracking vrrps to track the interface of each tracked address */ list_for_each_entry(addr, &vrrp_data->static_addresses, e_list) { if (!addr->track_group) continue; if (addr->dont_track) { log_message(LOG_INFO, "Static address has both track_group and no_track set - not tracking"); continue; } list_for_each_entry(top, &addr->track_group->vrrp_instances, e_list) add_vrrp_to_interface(top->obj.vrrp, addr->ifp, 0, false, false, TRACK_SADDR); } /* Add the tracking vrrps to track the interface of each tracked address */ list_for_each_entry(route, &vrrp_data->static_routes, e_list) { if (!route->track_group) continue; if (route->dont_track) { log_message(LOG_INFO, "Static route has both track_group and no_track set - not tracking"); continue; } list_for_each_entry(top, &route->track_group->vrrp_instances, e_list) { if (route->oif) add_vrrp_to_interface(top->obj.vrrp, route->oif, 0, false, false, TRACK_SROUTE); } } list_for_each_entry(rule, &vrrp_data->static_rules, e_list) { if (!rule->track_group) continue; if (rule->dont_track) { log_message(LOG_INFO, "Static rule has both track_group and no_track set - not tracking"); continue; } list_for_each_entry(top, &rule->track_group->vrrp_instances, e_list) { if (rule->iif) add_vrrp_to_interface(top->obj.vrrp, rule->iif, 0, false, false, TRACK_SRULE); } } } void static_track_group_reinstate_config(interface_t *ifp) { ip_address_t *addr; ip_route_t *route; list_for_each_entry(addr, &vrrp_data->static_addresses, e_list) { if (addr->dont_track) continue; if (addr->ifp != ifp) continue; reinstate_static_address(addr); } /* Add the tracking vrrps to track the interface of each tracked address */ list_for_each_entry(route, &vrrp_data->static_routes, e_list) { if (route->dont_track) continue; if (route->oif != ifp) continue; reinstate_static_route(route); } /* Rules don't get deleted on interface deletion, so we don't need to do anything for them list_for_each_entry(rule, &vrrp_data->static_rules, e_list) { if (rule->dont_track) continue; if (rule->iif != ifp) continue; reinstate_static_route(route); } */ } keepalived-2.3.3/keepalived/vrrp/vrrp_json.c0000664000175000017500000002707114401326157014615 /* * 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, * Damien Clabaut, * * 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, */ #include "config.h" #include "vrrp_json.h" #include #include #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 "global_data.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; } 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; } 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; } #ifdef _WITH_TRACK_PROCESS_ static int vrrp_json_track_process_dump(json_writer_t *wr, list_head_t *e) { tracked_process_t *tpr = list_entry(e, tracked_process_t, e_list); vrrp_tracked_process_t *vprocess = tpr->process; jsonw_string(wr, vprocess->pname); return 0; } #endif 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; } #if defined _WITH_VRRP_AUTH_ static int vrrp_json_auth_dump(json_writer_t *wr, const char *prop, vrrp_t *vrrp) { char buf[sizeof(vrrp->auth_data) + 1]; 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; } #endif 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", __test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags)); jsonw_uint_field(wr, "skip_check_adv_addr", __test_bit(VRRP_FLAG_SKIP_CHECK_ADV_ADDR, &vrrp->flags)); 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_DOUBLE); 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", __test_bit(VRRP_FLAG_PROMOTE_SECONDARIES, &vrrp->flags)); jsonw_float_field(wr, "adver_int", vrrp->adver_int / TIMER_HZ_DOUBLE); jsonw_float_field(wr, "master_adver_int", vrrp->master_adver_int / TIMER_HZ_DOUBLE); #ifdef _WITH_FIREWALL_ jsonw_uint_field(wr, "accept", vrrp->accept); #endif jsonw_bool_field(wr, "nopreempt", __test_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags)); 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); vrrp_json_array_dump(wr, "vroutes", &vrrp->vroutes, vrrp_json_vroute_dump); vrrp_json_array_dump(wr, "vrules", &vrrp->vrules, vrrp_json_vrule_dump); /* 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_TRACK_PROCESS_ vrrp_json_array_dump(wr, "track_process", &vrrp->track_process, vrrp_json_track_process_dump); #endif #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; } #ifdef _WITH_TRACK_PROCESS_ static int vrrp_json_vprocess_dump(json_writer_t *wr, list_head_t *e) { vrrp_tracked_process_t *vprocess = list_entry(e, vrrp_tracked_process_t, e_list); char *params, *p; jsonw_start_object(wr); jsonw_string_field(wr, "process", vprocess->pname); if (vprocess->process_params) { params = MALLOC(vprocess->process_params_len); memcpy(params, vprocess->process_params, vprocess->process_params_len); p = params; for (p = strchr(params, '\0'); p < params + vprocess->process_params_len - 1; p = strchr(params + 1, '\0')) *p = ' '; jsonw_string_field(wr, "parameters", params); FREE(params); } jsonw_string_field(wr, "param_match", vprocess->param_match == PARAM_MATCH_NONE ? "none" : vprocess->param_match == PARAM_MATCH_EXACT ? "exact" : vprocess->param_match == PARAM_MATCH_PARTIAL ? "partial" : vprocess->param_match == PARAM_MATCH_INITIAL ? "initial" : "unknown"); jsonw_uint_field(wr, "min_processes", vprocess->quorum); if (vprocess->quorum_max < UINT_MAX) jsonw_uint_field(wr, "max_processes", vprocess->quorum_max); jsonw_uint_field(wr, "current_processes", vprocess->num_cur_proc); jsonw_bool_field(wr, "have_quorum", vprocess->have_quorum); jsonw_int_field(wr, "weight", vprocess->weight_reverse ? -(int)vprocess->weight : vprocess->weight); jsonw_float_field(wr, "terminate_delay", (double)vprocess->terminate_delay / TIMER_HZ); jsonw_float_field(wr, "fork_delay", (double)vprocess->fork_delay / TIMER_HZ); jsonw_bool_field(wr, "fork_delay_timer_running", vprocess->fork_timer_thread); jsonw_bool_field(wr, "terminate_delay_timer_running", vprocess->terminate_timer_thread); jsonw_bool_field(wr, "full_command", vprocess->full_command); jsonw_end_object(wr); return 0; } static int vrrp_json_vprocesses_dump(json_writer_t *wr) { vrrp_json_array_dump(wr, "track_process", &vrrp_data->vrrp_track_processes, vrrp_json_vprocess_dump); return 0; } #endif /* * 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); if (global_data->json_version == JSON_VERSION_V2) { jsonw_start_object(wr); jsonw_name(wr, "vrrp"); } 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); if (global_data->json_version == JSON_VERSION_V2) { #ifdef _WITH_TRACK_PROCESS_ if (!list_empty(&vrrp_data->vrrp_track_processes)) vrrp_json_vprocesses_dump(wr); #endif jsonw_end_object(wr); } jsonw_destroy(&wr); return 0; } void vrrp_print_json(void) { FILE *fp; const char *filename; if (list_empty(&vrrp_data->vrrp)) return; filename = make_tmp_filename("keepalived.json"); fp = fopen_safe(filename, "w"); if (fp) { vrrp_json_dump(fp); fclose(fp); } else log_message(LOG_INFO, "Can't open %s/keepalived.json (%d: %m)", tmp_dir, errno); FREE_CONST(filename); } keepalived-2.3.3/keepalived/vrrp/vrrp_scheduler.c0000664000175000017500000013553214770042064015625 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Sheduling framework for vrrp code. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #if defined _WITH_VRRP_AUTH_ #include #endif #include #include #include #include #include "vrrp_scheduler.h" #include "vrrp_track.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_vmac.h" #endif #include "vrrp_sync.h" #include "vrrp_notify.h" #include "vrrp_data.h" #include "vrrp_arp.h" #include "vrrp_ndisc.h" #include "vrrp_if.h" #include "global_data.h" #include "memory.h" #include "list_head.h" #include "logger.h" #include "main.h" #include "signals.h" #include "utils.h" #include "bitops.h" #include "vrrp_sock.h" #ifdef _WITH_SNMP_RFCV3_ #include "vrrp_snmp.h" #endif #ifdef _WITH_BFD_ #include "bfd_event.h" #include "bfd_daemon.h" #endif #ifdef THREAD_DUMP #include "scheduler.h" #endif #ifdef _WITH_LVS_ #include "ipvswrapper.h" #endif #include "keepalived_netlink.h" /* For load testing recvmsg() */ /* #define DEBUG_RECVMSG */ /* For _RECVMSG_DEBUG_ we want load testing code as well */ #ifdef _RECVMSG_DEBUG_ #define DEBUG_RECVMSG 1 #endif /* global vars */ bool vrrp_initialised; timeval_t vrrp_delayed_start_time; #ifdef _TSM_DEBUG_ bool do_tsm_debug; #endif #ifdef _RECVMSG_DEBUG_ bool do_recvmsg_debug; bool do_recvmsg_debug_dump; #endif /* local variables */ #ifdef _WITH_BFD_ static thread_ref_t bfd_thread; /* BFD control pipe read thread */ #endif /* VRRP FSM (Finite State Machine) design. * * The state transition diagram implemented is : * * +---------------+ * +----------------| |----------------+ * | | Fault | | * | +------------>| |<------------+ | * | | +---------------+ | | * | | | | | * | | V | | * | | +---------------+ | | * | | +--------->| |<---------+ | | * | | | | Initialize | | | | * | | | +-------| |-------+ | | | * | | | | +---------------+ | | | | * | | | | | | | | * V | | V V | | V * +---------------+ +---------------+ * | |---------------------->| | * | Master | | Backup | * | |<----------------------| | * +---------------+ +---------------+ */ static void vrrp_script_child_thread(thread_ref_t); static void vrrp_script_thread(thread_ref_t); #ifdef _WITH_BFD_ static void vrrp_bfd_thread(thread_ref_t); #endif static void vrrp_read_dispatcher_thread(thread_ref_t); /* VRRP TSM (Transition State Matrix) design. * * Introducing the Synchronization extension to VRRP * protocol, introduce the need for a transition machinery. * This mechanism can be designed using a diagonal matrix. * We call this matrix the VRRP TSM: * * \ E | B | M | F | * S \ | | | | * ------+-----+-----+-----+ Legend: * B | x 1 2 | B: VRRP BACKUP state * ------+ | M: VRRP MASTER state * M | 3 x 4 | F: VRRP FAULT state * ------+ | S: VRRP start state (before transition) * F | 5 6 x | E: VRRP end state (after transition) * ------+-----------------+ [1..6]: Handler functions. * * So we have have to implement n(n-1) handlers in order to deal with * all transitions possible. This matrix defines the maximum handlers * to implement for having the most time optimized transition machine. * For example: * . The handler (1) will sync all the BACKUP VRRP instances of a * group to MASTER state => we will call it vrrp_sync_master. * .... and so on for all other state .... * * This matrix is the strict implementation way. For readability and * performance we have implemented some handlers directly into the VRRP * FSM or they are handled when the trigger events to/from FAULT state occur. * For instance the handlers (2), (4), (5) & (6) are handled when it is * detected that a script or an interface has failed or recovered since * it will speed up convergence to init state. * Additionaly, we have implemented some other handlers into the matrix * in order to speed up group synchronization takeover. For instance * transition: * o B->B: To catch wantstate MASTER transition to force sync group * to this transition state too. * o F->F: To speed up FAULT state transition if group is not already * synced to FAULT state. */ static struct { void (*handler) (vrrp_t *); } VRRP_TSM[VRRP_MAX_TSM_STATE + 1][VRRP_MAX_TSM_STATE + 1] = { /* From: To: > BACKUP MASTER FAULT */ /* v */ { {NULL}, {NULL}, {NULL}, {NULL} }, /* BACKUP */ { {NULL}, {NULL}, {vrrp_sync_master}, {NULL} }, /* MASTER */ { {NULL}, {vrrp_sync_backup}, {vrrp_sync_master}, {NULL} }, /* FAULT */ { {NULL}, {NULL}, {vrrp_sync_master}, {NULL} } }; /* * Initialize state handling * --rfc2338.6.4.1 */ static void vrrp_init_state(list_head_t *l) { vrrp_t *vrrp; vrrp_sgroup_t *vgroup; bool is_up; int new_state; /* We can send SMTP messages from this point, so set the time */ set_time_now(); /* Do notifications for any sync groups in fault or backup state */ list_for_each_entry(vgroup, &vrrp_data->vrrp_sync_group, e_list) { /* Init group if needed */ if ((vgroup->state == VRRP_STATE_FAULT || vgroup->state == VRRP_STATE_BACK) && !vgroup->state_same_at_reload) send_group_notifies(vgroup); else if (reload && global_data->fifo_write_vrrp_states_on_reload) notify_group_fifo(vgroup); vgroup->state_same_at_reload = false; } list_for_each_entry(vrrp, l, e_list) { int vrrp_begin_state = vrrp->state; /* wantstate is the state we would be in disregarding any sync group */ if (vrrp->state == VRRP_STATE_FAULT) vrrp->wantstate = VRRP_STATE_FAULT; new_state = vrrp->sync ? vrrp->sync->state : vrrp->wantstate; is_up = VRRP_ISUP(vrrp); if (is_up && new_state == VRRP_STATE_MAST && !vrrp->num_script_init && (!vrrp->sync || !vrrp->sync->num_member_init) && (vrrp->base_priority == VRRP_PRIO_OWNER || vrrp->reload_master) && vrrp->wantstate == VRRP_STATE_MAST) { #ifdef _WITH_LVS_ /* Check if sync daemon handling is needed */ if (global_data->lvs_syncd.ifname && global_data->lvs_syncd.vrrp == vrrp && !global_data->lvs_syncd.daemon_set_reload) ipvs_syncd_cmd(IPVS_STARTDAEMON, &global_data->lvs_syncd, vrrp->state == VRRP_STATE_MAST ? IPVS_MASTER : IPVS_BACKUP, false); #endif if (!vrrp->reload_master) { #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->next_master_reason = VRRPV3_MASTER_REASON_PREEMPTED; #endif /* The simplest way to become master is to timeout from the backup state * very quickly (1usec) */ vrrp->state = VRRP_STATE_BACK; vrrp->ms_down_timer = 1; } // TODO Do we need -> vrrp_restore_interface(vrrp, false, false); // It removes everything, so probably if !reload } else { if (new_state == VRRP_STATE_BACK && vrrp->wantstate == VRRP_STATE_MAST) { /* We need to allow one adver_int to pass to ensure there is no other master */ vrrp->ms_down_timer = vrrp->master_adver_int + VRRP_TIMER_SKEW_MIN(vrrp); } else vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->next_master_reason = VRRPV3_MASTER_REASON_MASTER_NO_RESPONSE; #endif #ifdef _WITH_LVS_ /* Check if sync daemon handling is needed */ if (global_data->lvs_syncd.ifname && global_data->lvs_syncd.vrrp == vrrp && !global_data->lvs_syncd.daemon_set_reload) ipvs_syncd_cmd(IPVS_STARTDAEMON, &global_data->lvs_syncd, IPVS_BACKUP, false); #endif /* Set interface state */ netlink_error_ignore = ESRCH; // returned if route does not exist vrrp_restore_interface(vrrp, false, true); netlink_error_ignore = 0; if (is_up && new_state != VRRP_STATE_FAULT && !vrrp->num_script_init && (!vrrp->sync || !vrrp->sync->num_member_init)) { if (vrrp->state != VRRP_STATE_BACK) { log_message(LOG_INFO, "(%s) Entering BACKUP STATE (init)", vrrp->iname); vrrp->state = VRRP_STATE_BACK; } } else { /* Note: if we have alpha mode scripts, we enter fault state, but don't want * to log it here */ if (vrrp_begin_state != vrrp->state) log_message(LOG_INFO, "(%s) Entering FAULT STATE (init)", vrrp->iname); vrrp->state = VRRP_STATE_FAULT; } if (vrrp_begin_state != vrrp->state) vrrp->last_transition = timer_now(); if (vrrp_begin_state != vrrp->state && (vrrp->state != VRRP_STATE_FAULT || vrrp->flags_if_fault)) send_instance_notifies(vrrp); else if (reload && global_data->fifo_write_vrrp_states_on_reload) notify_instance_fifo(vrrp); } #ifdef _WITH_SNMP_RFC_ vrrp->stats->uptime = timer_now(); #endif } } /* Declare vrrp_timer_less() rbtree compare function */ RB_TIMER_LESS(vrrp, rb_sands); /* Compute the new instance sands */ void vrrp_init_instance_sands(vrrp_t *vrrp) { set_time_now(); if (vrrp->state == VRRP_STATE_MAST) { if (vrrp->reload_master) vrrp->sands = time_now; else vrrp->sands = timer_add_long(time_now, vrrp->adver_int); } else if (vrrp->state == VRRP_STATE_BACK) { /* * When in the BACKUP state the expiry timer should be updated to * time_now plus the Master Down Timer, when a non-preemptable packet is * received. */ if (vrrp_delayed_start_time.tv_sec) vrrp->sands = timer_add_long(vrrp_delayed_start_time, vrrp->ms_down_timer); else vrrp->sands = timer_add_long(time_now, vrrp->ms_down_timer); } else if (vrrp->state == VRRP_STATE_FAULT || vrrp->state == VRRP_STATE_INIT) vrrp->sands.tv_sec = TIMER_DISABLED; rb_move_cached(&vrrp->rb_sands, &vrrp->sockets->rb_sands, vrrp_timer_less); } static void vrrp_init_sands(list_head_t *l) { vrrp_t *vrrp; list_for_each_entry(vrrp, l, e_list) { vrrp->sands.tv_sec = TIMER_DISABLED; rb_add_cached(&vrrp->rb_sands, &vrrp->sockets->rb_sands, vrrp_timer_less); vrrp_init_instance_sands(vrrp); vrrp->reload_master = false; } } static void vrrp_init_script(list_head_t *l) { vrrp_script_t *vscript; list_for_each_entry(vscript, l, e_list) { if (vscript->init_state == SCRIPT_INIT_STATE_INIT || vscript->init_state == SCRIPT_INIT_STATE_INIT_RELOAD) vscript->result = vscript->rise - 1; /* one success is enough */ else if (vscript->init_state == SCRIPT_INIT_STATE_FAILED) vscript->result = 0; /* assume failed by config */ thread_add_event(master, vrrp_script_thread, vscript, (int)vscript->interval); } } /* Timer functions */ static timeval_t * vrrp_compute_timer(const sock_t *sock) { vrrp_t *vrrp; /* The sock won't exist if there isn't a vrrp instance on it, * so rb_first will always exist. */ vrrp = rb_entry(rb_first_cached(&sock->rb_sands), vrrp_t, rb_sands); return &vrrp->sands; } void vrrp_thread_requeue_read(vrrp_t *vrrp) { thread_requeue_read(master, vrrp->sockets->fd_in, vrrp_compute_timer(vrrp->sockets)); } /* Thread functions */ static void vrrp_register_workers(list_head_t *l) { sock_t *sock; timeval_t timer; /* Init compute timer */ memset(&timer, 0, sizeof(timer)); /* Init the VRRP instances state */ vrrp_init_state(&vrrp_data->vrrp); /* Init VRRP instances sands */ vrrp_init_sands(&vrrp_data->vrrp); /* Init VRRP tracking scripts */ if (!list_empty(&vrrp_data->vrrp_script)) vrrp_init_script(&vrrp_data->vrrp_script); #ifdef _WITH_BFD_ if (!list_empty(&vrrp_data->vrrp)) { // TODO - should we only do this if we have track_bfd? Probably not /* Init BFD tracking thread */ bfd_thread = thread_add_read(master, vrrp_bfd_thread, NULL, bfd_vrrp_event_pipe[0], TIMER_NEVER, 0); } #endif /* Register VRRP workers threads */ list_for_each_entry(sock, l, e_list) { /* Register a timer thread if interface exists */ if (sock->fd_in != -1) sock->thread = thread_add_read_sands(master, vrrp_read_dispatcher_thread, sock, sock->fd_in, vrrp_compute_timer(sock), 0); } } void vrrp_thread_add_read(vrrp_t *vrrp) { vrrp->sockets->thread = thread_add_read_sands(master, vrrp_read_dispatcher_thread, vrrp->sockets, vrrp->sockets->fd_in, vrrp_compute_timer(vrrp->sockets), 0); } /* VRRP dispatcher functions */ static sock_t * __attribute__ ((pure)) already_exist_sock(const list_head_t *l, sa_family_t family, int proto, const interface_t *ifp, #ifdef _HAVE_VRF_ const interface_t *vrf_ifp, #endif const sockaddr_t *mcast_daddr, const sockaddr_t *unicast_src) { sock_t *sock; list_for_each_entry(sock, l, e_list) { if ((sock->family == family) && (sock->proto == proto) && (sock->ifp == ifp) && #ifdef _HAVE_VRF_ (sock->vrf_ifp == vrf_ifp) && #endif (!unicast_src == !sock->unicast_src) && ((!unicast_src && !inet_sockaddrcmp(sock->mcast_daddr, mcast_daddr)) || (unicast_src && !inet_sockaddrcmp(sock->unicast_src, unicast_src)))) return sock; } return NULL; } static sock_t * alloc_sock(list_head_t *l, sa_family_t family, int proto, interface_t *ifp, #ifdef _HAVE_VRF_ const interface_t *vrf_ifp, #endif sockaddr_t *mcast_daddr, sockaddr_t *unicast_src) { sock_t *new; PMALLOC(new); INIT_LIST_HEAD(&new->e_list); new->family = family; new->proto = proto; if (unicast_src) new->unicast_src = unicast_src; else new->mcast_daddr = mcast_daddr; new->ifp = ifp; #ifdef _HAVE_VRF_ new->vrf_ifp = vrf_ifp; #endif new->rb_vrid = RB_ROOT; new->rb_sands = RB_ROOT_CACHED; list_add_tail(&new->e_list, l); return new; } static inline int vrrp_vrid_cmp(const void *vrid, const rb_node_t *a) { return less_equal_greater_than(*PTR_CAST_CONST(uint8_t, vrid), rb_entry_const(a, vrrp_t, rb_vrid)->vrid); } static inline bool vrrp_vrid_less(rb_node_t *a, const rb_node_t *b) { return rb_entry(a, vrrp_t, rb_vrid)->vrid < rb_entry_const(b, vrrp_t, rb_vrid)->vrid; } static void vrrp_create_sockpool(list_head_t *l) { vrrp_t *vrrp; interface_t *ifp; int proto; sock_t *sock; sockaddr_t *unicast_src; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) unicast_src = NULL; else unicast_src = &vrrp->saddr; ifp = #ifdef _HAVE_VRRP_VMAC_ (__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags)) ? vrrp->configured_ifp : #endif vrrp->ifp; proto = IPPROTO_VRRP; #if defined _WITH_VRRP_AUTH_ if (vrrp->auth_type == VRRP_AUTH_AH) proto = IPPROTO_AH; #endif /* add the vrrp element if not exist */ if (!(sock = already_exist_sock(l, vrrp->family, proto, ifp, #ifdef _HAVE_VRF_ vrrp->vrf_ifp, #endif &vrrp->mcast_daddr, unicast_src))) sock = alloc_sock(l, vrrp->family, proto, ifp, #ifdef _HAVE_VRF_ vrrp->vrf_ifp, #endif &vrrp->mcast_daddr, unicast_src); /* Add the vrrp_t indexed by vrid to the socket */ rb_add(&vrrp->rb_vrid, &sock->rb_vrid, vrrp_vrid_less); if (vrrp->kernel_rx_buf_size) sock->rx_buf_size += vrrp->kernel_rx_buf_size; else if (global_data->vrrp_rx_bufs_policy & RX_BUFS_SIZE) sock->rx_buf_size += global_data->vrrp_rx_bufs_size; else if (global_data->vrrp_rx_bufs_policy & RX_BUFS_POLICY_ADVERT) sock->rx_buf_size += global_data->vrrp_rx_bufs_multiples * vrrp_adv_len(vrrp); else if (vrrp->ifp && global_data->vrrp_rx_bufs_policy & RX_BUFS_POLICY_MTU) sock->rx_buf_size += global_data->vrrp_rx_bufs_multiples * vrrp->ifp->mtu; } } static void vrrp_open_sockpool(list_head_t *l) { sock_t *sock; list_for_each_entry(sock, l, e_list) { if ((sock->ifp && !sock->ifp->ifindex) #ifdef _HAVE_VRF_ || (sock->vrf_ifp && !sock->vrf_ifp->ifindex) #endif ) { sock->fd_in = sock->fd_out = -1; continue; } /* coverity[var_deref_model] */ open_sockpool_socket(sock); } } static void vrrp_set_fds(list_head_t *l) { sock_t *sock; vrrp_t *vrrp; list_for_each_entry(sock, l, e_list) { rb_for_each_entry(vrrp, &sock->rb_vrid, rb_vrid) vrrp->sockets = sock; } } /* * We create & allocate a socket pool here. The soft design * can be sum up by the following sketch : * * fd1 fd2 fd3 fd4 fdi fdi+1 * -----\__/--------\__/---........---\__/--- * | ETH0 | | ETH1 | | ETHn | * +------+ +------+ +------+ * * TODO TODO - this description is way out of date * Here we have n physical NIC. Each NIC own a maximum of 2 fds. * (one for VRRP the other for IPSEC_AH). All our VRRP instances * are multiplexed through this fds. So our design can handle 2*n * multiplexing points. */ void vrrp_dispatcher_init(__attribute__((unused)) thread_ref_t thread) { vrrp_create_sockpool(&vrrp_data->vrrp_socket_pool); /* open the VRRP socket pool */ vrrp_open_sockpool(&vrrp_data->vrrp_socket_pool); /* set VRRP instance fds to sockpool */ vrrp_set_fds(&vrrp_data->vrrp_socket_pool); /* create the VRRP socket pool list */ /* register read dispatcher worker thread */ vrrp_register_workers(&vrrp_data->vrrp_socket_pool); /* Dump socket pool */ if (__test_bit(LOG_DETAIL_BIT, &debug)) dump_sock_list(NULL, &vrrp_data->vrrp_socket_pool); vrrp_initialised = true; UNSET_RELOAD; } #ifdef _WITH_BFD_ void cancel_vrrp_threads(void) { if (bfd_thread) { thread_cancel(bfd_thread); bfd_thread = NULL; } } #endif void vrrp_dispatcher_release(vrrp_data_t *data) { free_sock_list(&data->vrrp_socket_pool); #ifdef _WITH_BFD_ cancel_vrrp_threads(); #endif } static void vrrp_goto_master(vrrp_t * vrrp) { /* handle master state transition */ vrrp->wantstate = VRRP_STATE_MAST; vrrp_state_goto_master(vrrp); } /* Delayed gratuitous ARP thread */ void vrrp_gratuitous_arp_thread(thread_ref_t thread) { vrrp_t *vrrp = THREAD_ARG(thread); /* Simply broadcast the gratuitous ARP */ vrrp_send_link_update(vrrp, vrrp->garp_rep); } /* Delayed gratuitous ARP thread after receiving a lower priority advert */ void vrrp_lower_prio_gratuitous_arp_thread(thread_ref_t thread) { vrrp_t *vrrp = THREAD_ARG(thread); /* Simply broadcast the gratuitous ARP */ vrrp_send_link_update(vrrp, vrrp->garp_lower_prio_rep); } /* Gratuitous ARP refresh thread (i.e. periodic send of GARP messages) */ void vrrp_gratuitous_arp_refresh_thread(thread_ref_t thread) { vrrp_t *vrrp = THREAD_ARG(thread); vrrp_send_link_update(vrrp, vrrp->garp_refresh_rep); thread_add_timer(master, vrrp_gratuitous_arp_refresh_thread, vrrp, timer_long(vrrp->garp_refresh)); } #ifdef _HAVE_VRRP_VMAC_ /* Gratuitous ARP VMAC update thread (i.e. one GARP per VMAC interface * on which VIPs are configured. */ void vrrp_gratuitous_arp_vmac_update_thread(thread_ref_t thread) { vrrp_t *vrrp = THREAD_ARG(thread); vrrp_send_vmac_update(vrrp); thread_add_timer(master, vrrp_gratuitous_arp_vmac_update_thread, vrrp, timer_long(vrrp->vmac_garp_intvl)); } #endif void try_up_instance(vrrp_t *vrrp, bool leaving_init, vrrp_fault_fl_t resolved_flag) { int wantstate; ip_address_t ip_addr = {0}; /* We can not use try_up_instance() for several resolution * at the same time */ #ifdef _FAULT_FLAGS_CHECK_ if (resolved_flag != VRRP_FAULT_FL_TRACKER && !__test_bit(resolved_flag, &vrrp->flags_if_fault)) log_message(LOG_INFO, "(%s) BUG - try_up_instance flag %u not set in 0x%lx, leaving_init %d", vrrp->iname, resolved_flag, vrrp->flags_if_fault, leaving_init); if (!__test_bit(VRRP_FAULT_FL_TRACKER, &vrrp->flags_if_fault) != !vrrp->num_track_fault) log_message(LOG_INFO, "(%s) BUG - clear_fault - tracker flag 0x%lx does not match num_track_fault %u", vrrp->iname, vrrp->flags_if_fault, vrrp->num_track_fault); #endif if (leaving_init) { if (vrrp->flags_if_fault) return; } else { if (resolved_flag == VRRP_FAULT_FL_TRACKER) { if (!--vrrp->num_track_fault) __clear_bit(VRRP_FAULT_FL_TRACKER, &vrrp->flags_if_fault); } else __clear_bit(resolved_flag, &vrrp->flags_if_fault); } if (vrrp->flags_if_fault) return; if (vrrp->num_script_init) { if (!vrrp->flags_if_fault) { if (vrrp->sync) { vrrp->sync->num_member_fault--; vrrp->sync->state = VRRP_STATE_INIT; } vrrp->wantstate = VRRP_STATE_BACK; } return; } if (vrrp->wantstate == VRRP_STATE_MAST && vrrp->base_priority == VRRP_PRIO_OWNER) { #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->next_master_reason = VRRPV3_MASTER_REASON_PREEMPTED; #endif } else { vrrp->wantstate = VRRP_STATE_BACK; #ifdef _WITH_SNMP_RFCV3_ vrrp->stats->next_master_reason = VRRPV3_MASTER_REASON_MASTER_NO_RESPONSE; #endif } vrrp->master_adver_int = vrrp->adver_int; if (vrrp->wantstate == VRRP_STATE_MAST && vrrp->base_priority == VRRP_PRIO_OWNER) vrrp->ms_down_timer = vrrp->master_adver_int + VRRP_TIMER_SKEW(vrrp); else vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); if (vrrp->sync) { if (leaving_init) { if (vrrp->sync->num_member_fault) return; } else if (--vrrp->sync->num_member_fault || vrrp->sync->num_member_init) return; } /* If the sync group can't go to master, we must go to backup state */ wantstate = vrrp->wantstate; if (vrrp->sync && vrrp->wantstate == VRRP_STATE_MAST && !vrrp_sync_can_goto_master(vrrp)) vrrp->wantstate = VRRP_STATE_BACK; /* We can come up */ vrrp_state_leave_fault(vrrp); /* If we are using unicast, the master may have lost us from its ARP cache. * We want to renew the ARP cache on the master, so that it can send adverts * to us straight away, without a delay before it sends an ARP request message * and we respond. If we don't do this, we can time out and transition to master * before the master renews its ARP entry, since the master cannot send us adverts * until it has done so. */ if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) && vrrp->ifp && vrrp->saddr.ss_family != AF_UNSPEC) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "%s: sending gratuitous %s for %s", vrrp->iname, vrrp->family == AF_INET ? "ARP" : "NA", inet_sockaddrtos(&vrrp->saddr)); ip_addr.ifp = IF_BASE_IFP(vrrp->ifp); if (vrrp->saddr.ss_family == AF_INET) { ip_addr.u.sin.sin_addr.s_addr = PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr.s_addr; send_gratuitous_arp_immediate(ip_addr.ifp, &ip_addr); } else { /* IPv6 */ ip_addr.u.sin6_addr = PTR_CAST(struct sockaddr_in6, &vrrp->saddr)->sin6_addr; ndisc_send_unsolicited_na_immediate(ip_addr.ifp, &ip_addr); } } vrrp_init_instance_sands(vrrp); vrrp_thread_requeue_read(vrrp); vrrp->wantstate = wantstate; if (vrrp->sync) { if (vrrp->state == VRRP_STATE_MAST) vrrp_sync_master(vrrp); else vrrp_sync_backup(vrrp); } } #ifdef _WITH_BFD_ static void vrrp_handle_bfd_event(bfd_event_t * evt) { vrrp_tracked_bfd_t *vbfd; tracking_obj_t *tbfd; vrrp_t * vrrp; struct timeval cur_time; struct timeval timer_tmp; uint32_t delivery_time; if (__test_bit(LOG_DETAIL_BIT, &debug)) { cur_time = timer_now(); timersub(&cur_time, &evt->sent_time, &timer_tmp); delivery_time = timer_long(timer_tmp); log_message(LOG_INFO, "Received BFD event: instance %s is in" " state %s (delivered in %" PRIu32 " usec)", evt->iname, BFD_STATE_STR(evt->state), delivery_time); } list_for_each_entry(vbfd, &vrrp_data->vrrp_track_bfds, e_list) { if (strcmp(vbfd->bname, evt->iname)) continue; if ((vbfd->bfd_up && evt->state == BFD_STATE_UP) || (!vbfd->bfd_up && evt->state == BFD_STATE_DOWN)) continue; vbfd->bfd_up = (evt->state == BFD_STATE_UP); list_for_each_entry(tbfd, &vbfd->tracking_vrrp, e_list) { vrrp = tbfd->obj.vrrp; log_message(LOG_INFO, "VRRP_Instance(%s) Tracked BFD" " instance %s is %s", vrrp->iname, evt->iname, vbfd->bfd_up ? "UP" : "DOWN"); if (tbfd->weight) { if (vbfd->bfd_up) vrrp->total_priority += abs(tbfd->weight) * tbfd->weight_multiplier; else vrrp->total_priority -= abs(tbfd->weight) * tbfd->weight_multiplier; vrrp_set_effective_priority(vrrp); continue; } if (!!vbfd->bfd_up == (tbfd->weight_multiplier == 1)) try_up_instance(vrrp, false, VRRP_FAULT_FL_TRACKER); else down_instance(vrrp, VRRP_FAULT_FL_TRACKER); } break; } } static void vrrp_bfd_thread(thread_ref_t thread) { bfd_event_t evt; if (thread->type == THREAD_READ_ERROR) { thread_close_fd(thread); return; } bfd_thread = thread_add_read(master, vrrp_bfd_thread, NULL, thread->u.f.fd, TIMER_NEVER, 0); if (thread->type != THREAD_READY_READ_FD) return; while (read(thread->u.f.fd, &evt, sizeof(bfd_event_t)) != -1) vrrp_handle_bfd_event(&evt); } #endif /* Handle dispatcher read timeout */ static int vrrp_dispatcher_read_timeout(sock_t *sock) { vrrp_t *vrrp; int prev_state; set_time_now(); rb_for_each_entry_cached(vrrp, &sock->rb_sands, rb_sands) { if (vrrp->sands.tv_sec == TIMER_DISABLED || timercmp(&vrrp->sands, &time_now, >)) break; prev_state = vrrp->state; if (vrrp->state == VRRP_STATE_BACK) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Receive advertisement timeout", vrrp->iname); vrrp_goto_master(vrrp); } else if (vrrp->state == VRRP_STATE_MAST) vrrp_state_master_tx(vrrp); /* handle instance synchronization */ #ifdef _TSM_DEBUG_ if (do_tsm_debug) log_message(LOG_INFO, "Send [%s] TSM transition : [%d,%d] Wantstate = [%d]", vrrp->iname, prev_state, vrrp->state, vrrp->wantstate); #endif VRRP_TSM_HANDLE(prev_state, vrrp); vrrp_init_instance_sands(vrrp); } return sock->fd_in; } /* Handle dispatcher read packet */ static int vrrp_dispatcher_read(sock_t *sock) { vrrp_t *vrrp; rb_node_t *vrrp_node; const vrrphdr_t *hd; ssize_t len = 0; int prev_state = 0; sockaddr_t src_addr = { .ss_family = AF_UNSPEC }; #ifdef _NETWORK_TIMESTAMP_ char control_buf[128] __attribute__((aligned(__alignof__(struct cmsghdr)))); #else char control_buf[64] __attribute__((aligned(__alignof__(struct cmsghdr)))); #endif struct iovec iovec = { .iov_base = vrrp_buffer, .iov_len = vrrp_buffer_len }; struct msghdr msghdr = { .msg_name = &src_addr, .msg_namelen = sizeof(src_addr), .msg_iov = &iovec, .msg_iovlen = 1, .msg_control = control_buf, .msg_controllen = sizeof(control_buf) }; struct cmsghdr *cmsg; bool expected_cmsg; unsigned eintr_count; unsigned long rx_vrid_map[BIT_WORD(256 + BIT_PER_LONG - 1)] = { 0 }; bool terminate_receiving = false; #ifdef DEBUG_RECVMSG unsigned recv_data_count = 0; #endif const struct iphdr *iph; unicast_peer_t *unicast_peer; /* Strategy here is to handle incoming adverts pending into socket recvq * but stop if receive 2nd advert for a VRID on socket (this applies to * both configured and unconfigured VRIDs). * Seems a good tradeoff while simulating */ while (!terminate_receiving) { /* read & affect received buffer */ eintr_count = 0; while ((len = recvmsg(sock->fd_in, &msghdr, MSG_TRUNC | MSG_CTRUNC)) == -1 && check_EINTR(errno) && eintr_count++ < 10); if (len < 0) { #ifdef DEBUG_RECVMSG #ifdef _RECVMSG_DEBUG_ if (do_recvmsg_debug && (!recv_data_count || !check_EAGAIN(errno))) log_message(LOG_INFO, "recvmsg(%d) returned errno %d, %u eintr", sock->fd_in, errno, eintr_count); #endif #ifdef _RECVMSG_DEBUG_ if (do_recvmsg_debug) #endif { if (check_EINTR(errno)) log_message(LOG_INFO, "recvmsg(%d) looped %u times due to EINTR before terminating loop" , sock->fd_in, eintr_count); } #endif if (!check_EAGAIN(errno)) log_message(LOG_INFO, "recvmsg(%d) returned %d (%m)" , sock->fd_in, errno); #ifdef DEBUG_RECVMSG else if ( #ifdef _RECVMSG_DEBUG_ do_recvmsg_debug && #endif recv_data_count == 0) log_message(LOG_INFO, "recvmsg(%d) returned EAGAIN without any data being received" , sock->fd_in); #ifdef _RECVMSG_DEBUG_ if (do_recvmsg_debug) #endif { if (recv_data_count != 1) log_message(LOG_INFO, "recvmsg(%d) loop received %u packets" , sock->fd_in, recv_data_count); } #endif break; } #ifdef _RECVMSG_DEBUG_ else if (do_recvmsg_debug) log_message(LOG_INFO, "recvmsg(%d) looped %u times due to EINTR before returning %zd bytes from %s" , sock->fd_in, eintr_count, len, inet_sockaddrtos(&src_addr)); #elif defined DEBUG_RECVMSG if (eintr_count) log_message(LOG_INFO, "recvmsg(%d) looped %u times due to EINTR before returning %ld" , sock->fd_in, eintr_count, len); #endif /* Don't attempt to process data if no data received */ if (len == 0) { log_message(LOG_INFO, "recvmsg(%d) returned data length 0", sock->fd_in); continue; } #ifdef _RECVMSG_DEBUG_ if (do_recvmsg_debug_dump) { log_buffer("Received data", vrrp_buffer, len); } #endif #ifdef DEBUG_RECVMSG recv_data_count++; #endif if (msghdr.msg_flags & MSG_TRUNC) { log_message(LOG_INFO, "recvmsg(%d) message truncated from %zd to %zu bytes" , sock->fd_in, len, vrrp_buffer_len); continue; } if (msghdr.msg_flags & MSG_CTRUNC) { log_message(LOG_INFO, "recvmsg(%d), control message truncated from %zu to %" PRI_MSG_CONTROLLEN " bytes" , sock->fd_in, sizeof(control_buf), msghdr.msg_controllen); msghdr.msg_controllen = 0; } if (vrrp_delayed_start_time.tv_sec) continue; /* Check the received data includes at least the IP, possibly * the AH header and the VRRP header */ if (!(hd = vrrp_get_header(sock->family, vrrp_buffer, len))) break; vrrp_node = rb_find(&hd->vrid, &sock->rb_vrid, vrrp_vrid_cmp); /* No instance found => ignore the advert */ if (!vrrp_node) { if (global_data->log_unknown_vrids) log_message(LOG_INFO, "Unknown VRID(%d) received on interface(%s). ignoring..." , hd->vrid, IF_NAME(sock->ifp)); continue; } vrrp = rb_entry(vrrp_node, vrrp_t, rb_vrid); /* Defense strategy here is to handle no more than one advert * per VRID in order to flush socket rcvq... * This is a best effort mitigation */ if (__test_and_set_bit_array(hd->vrid, rx_vrid_map)) terminate_receiving = true; if (__test_bit(VRRP_FLAG_UNICAST_DUPLICATE_VRID, &vrrp->flags)) { rb_node_t *first = vrrp_node; /* Save for second loop */ /* First check the address we last received an advert from. This is * an optimisation since we are most likely to receive an advert from * the same address as last time, and it saves searching all the peers. */ for (; vrrp_node; vrrp_node = rb_next_match(&hd->vrid, vrrp_node, vrrp_vrid_cmp)) { vrrp = rb_entry(vrrp_node, vrrp_t, rb_vrid); if (!inet_sockaddrcmp(&src_addr, &vrrp->pkt_saddr)) break; } if (!vrrp_node) { /* Loop through VRRP instances matching hd->vrid if unicast to match * src address of packet against configured peers */ for (vrrp_node = first; vrrp_node; vrrp_node = rb_next_match(&hd->vrid, vrrp_node, vrrp_vrid_cmp)) { vrrp = rb_entry(vrrp_node, vrrp_t, rb_vrid); list_for_each_entry(unicast_peer, &vrrp->unicast_peer, e_list) { if (inet_sockaddrcmp(&src_addr, &unicast_peer->address) == 0) break; if (list_is_last(&unicast_peer->e_list, &vrrp->unicast_peer)) { unicast_peer = NULL; break; } } /* We have found the matching peer */ if (unicast_peer) break; } if (!vrrp_node) { /* Do nothing and fail because we didn't match any good instance */ if (global_data->log_unknown_vrids) log_message(LOG_INFO, "Unknown VRID(%d) received on interface(%s) from %s. ignoring..." , hd->vrid, IF_NAME(sock->ifp), inet_sockaddrtos(&src_addr)); continue; } } } if (vrrp->state == VRRP_STATE_FAULT || vrrp->state == VRRP_STATE_INIT) { /* We just ignore a message received when we are in fault state or * not yet fully initialised */ continue; } /* Save non packet data */ vrrp->pkt_saddr = src_addr; vrrp->rx_ttl_hl = -1; /* Default to not received */ if (sock->family == AF_INET) { iph = PTR_CAST_CONST(struct iphdr, vrrp_buffer); vrrp->multicast_pkt = IN_MULTICAST(htonl(iph->daddr)); vrrp->rx_ttl_hl = iph->ttl; } else vrrp->multicast_pkt = false; for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) { expected_cmsg = false; if (cmsg->cmsg_level == IPPROTO_IPV6) { expected_cmsg = true; if (cmsg->cmsg_type == IPV6_HOPLIMIT && cmsg->cmsg_len - sizeof(struct cmsghdr) == sizeof(unsigned int)) vrrp->rx_ttl_hl = *PTR_CAST(unsigned int, CMSG_DATA(cmsg)); else if (cmsg->cmsg_type == IPV6_PKTINFO && cmsg->cmsg_len - sizeof(struct cmsghdr) == sizeof(struct in6_pktinfo)) vrrp->multicast_pkt = IN6_IS_ADDR_MULTICAST(&(PTR_CAST(struct in6_pktinfo, CMSG_DATA(cmsg)))->ipi6_addr); else expected_cmsg = false; } #ifdef _NETWORK_TIMESTAMP_ else if (do_network_timestamp && cmsg->cmsg_level == SOL_SOCKET) { struct timespec *ts = (void *)CMSG_DATA(cmsg); char time_buf[9]; expected_cmsg = true; if (cmsg->cmsg_type == SO_TIMESTAMPNS) { strftime(time_buf, sizeof time_buf, "%T", localtime(&ts->tv_sec)); log_message(LOG_INFO, "TIMESTAMPNS (socket %d - VRID %u) %s.%9.9" PRI_ts_nsec , sock->fd_in, hd->vrid, time_buf, ts->tv_nsec); } #if 0 if (cmsg->cmsg_type == SO_TIMESTAMP) { struct timeval *tv = (void *)CMSG_DATA(cmsg); log_message(LOG_INFO, "TIMESTAMP message (%d - %u) %" PRI_tv_sec ".%6.6" PRI_tv_usec , sock->fd_in, hd->vrid, tv->tv_sec, tv->tv_usec); } else if (cmsg->cmsg_type == SO_TIMESTAMPING) { struct timespec *ts = (void *)CMSG_DATA(cmsg); log_message(LOG_INFO, "TIMESTAMPING message (%d - %u) %" PRI_ts_sec ".%9.9" PRI_ts_nsec ", raw %" PRI_ts_sec ".%9.9" PRI_ts_nsec , sock->fd_in, hd->vrid, ts->tv_sec, ts->tv_nsec, (ts+2)->tv_sec, (ts+2)->tv_nsec); } #endif else expected_cmsg = false; } #endif if (!expected_cmsg) log_message(LOG_INFO, "fd %d, unexpected control msg len %" PRI_MSG_CONTROLLEN ", level %d, type %d" , sock->fd_in, cmsg->cmsg_len , cmsg->cmsg_level, cmsg->cmsg_type); } /* For multicast, we attempt to bind the socket to ::1 to stop receiving any (non ::1) * unicast packets, but if that fails we will receive unicast packets on the multicast socket, * so just discard them here. * For unicast sockets, if any other instance on the same interface is using multicast we * will also receive the multicast packets, so also discard them here. */ if (sock->family == AF_INET6 && vrrp->multicast_pkt == __test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) discarding %sicast packet on %sicast instance", vrrp->iname, vrrp->multicast_pkt ? "mult" : "un", __test_bit(VRRP_FLAG_UNICAST, &vrrp->flags) ? "un" : "mult"); continue; } prev_state = vrrp->state; if (vrrp->state == VRRP_STATE_BACK) vrrp_state_backup(vrrp, hd, vrrp_buffer, len); else if (vrrp->state == VRRP_STATE_MAST) { if (vrrp_state_master_rx(vrrp, hd, vrrp_buffer, len)) vrrp_state_leave_master(vrrp, false); } else log_message(LOG_INFO, "(%s) In dispatcher_read with state %d" , vrrp->iname, vrrp->state); /* handle instance synchronization */ #ifdef _TSM_DEBUG_ if (do_tsm_debug) log_message(LOG_INFO, "Read [%s] TSM transition : [%d,%d] Wantstate = [%d]" , vrrp->iname, prev_state, vrrp->state, vrrp->wantstate); #endif VRRP_TSM_HANDLE(prev_state, vrrp); /* If we have sent an advert, reset the timer */ if (vrrp->state != VRRP_STATE_MAST || !vrrp->lower_prio_no_advert) vrrp_init_instance_sands(vrrp); } return sock->fd_in; } /* Our read packet dispatcher */ static void vrrp_read_dispatcher_thread(thread_ref_t thread) { sock_t *sock; int fd; /* Fetch thread arg */ sock = THREAD_ARG(thread); /* Dispatcher state handler */ if (thread->type == THREAD_READ_TIMEOUT || sock->fd_in == -1) fd = vrrp_dispatcher_read_timeout(sock); else fd = vrrp_dispatcher_read(sock); /* register next dispatcher thread */ if (fd != -1) sock->thread = thread_add_read_sands(thread->master, vrrp_read_dispatcher_thread, sock, fd, vrrp_compute_timer(sock), 0); } static void vrrp_script_thread(thread_ref_t thread) { vrrp_script_t *vscript = THREAD_ARG(thread); int ret; /* Register next timer tracker */ thread_add_timer(thread->master, vrrp_script_thread, vscript, vscript->interval); if (vscript->state != SCRIPT_STATE_IDLE) { /* We don't want the system to be overloaded with scripts that we are executing */ log_message(LOG_INFO, "Track script %s is %s, expect idle - skipping run", vscript->sname, vscript->state == SCRIPT_STATE_RUNNING ? "already running" : "being timed out"); return; } /* Execute the script in a child process. Parent returns, child doesn't */ #ifdef _SCRIPT_DEBUG_ if (do_script_debug) log_message(LOG_INFO, "Running vrrp script %s", vscript->sname); #endif ret = system_call_script(thread->master, vrrp_script_child_thread, vscript, (vscript->timeout) ? vscript->timeout : vscript->interval, &vscript->script); if (!ret) vscript->state = SCRIPT_STATE_RUNNING; } static void vrrp_script_child_thread(thread_ref_t thread) { int wait_status; pid_t pid; vrrp_script_t *vscript = THREAD_ARG(thread); int sig_num; unsigned timeout = 0; const char *script_exit_type = NULL; bool script_success; const char *reason = NULL; int reason_code; if (thread->type == THREAD_CHILD_TIMEOUT) { pid = THREAD_CHILD_PID(thread); if (vscript->state == SCRIPT_STATE_RUNNING) { vscript->state = SCRIPT_STATE_REQUESTING_TERMINATION; #ifdef _SCRIPT_DEBUG_ if (do_script_debug) log_message(LOG_INFO, "Sending TERM to %d", pid); #endif sig_num = SIGTERM; timeout = 2; } else if (vscript->state == SCRIPT_STATE_REQUESTING_TERMINATION) { vscript->state = SCRIPT_STATE_FORCING_TERMINATION; sig_num = SIGKILL; #ifdef _SCRIPT_DEBUG_ if (do_script_debug) log_message(LOG_INFO, "Sending KILL 2 to %d", pid); #endif timeout = 2; } else if (vscript->state == SCRIPT_STATE_FORCING_TERMINATION) { log_message(LOG_INFO, "Script %s child (PID %d) failed to terminate after kill", vscript->sname, pid); sig_num = SIGKILL; timeout = 10; /* Give it longer to terminate */ #ifdef _SCRIPT_DEBUG_ if (do_script_debug) log_message(LOG_INFO, "Sending KILL 10 to %d", pid); #endif } #ifdef _SCRIPT_DEBUG_ else if (do_script_debug) log_message(LOG_INFO, "script state %u for pid %d", vscript->state, pid); #endif /* Kill it off. */ if (timeout) { /* If kill returns an error, we can't kill the process since either the process has terminated, * or we don't have permission. If we can't kill it, there is no point trying again. */ if (kill(-pid, sig_num)) { if (errno == ESRCH) { /* The process does not exist, and we should * have reaped its exit status, otherwise it * would exist as a zombie process. */ log_message(LOG_INFO, "Script %s child (PID %d) lost", vscript->sname, THREAD_CHILD_PID(thread)); #if defined _SCRIPT_DEBUG_ && defined THREAD_DUMP if (do_script_debug) dump_thread_data(thread->master, NULL); #endif vscript->state = SCRIPT_STATE_IDLE; timeout = 0; } else { log_message(LOG_INFO, "kill -%d of process %s(%d) with new state %u failed with errno %d", sig_num, vscript->script.path ? vscript->script.path : vscript->script.args[0], pid, vscript->state, errno); timeout = 1000; } } } else if (vscript->state != SCRIPT_STATE_IDLE) { log_message(LOG_INFO, "Script %s child thread pid %d timeout with unknown script state %u", vscript->sname, pid, vscript->state); timeout = 10; /* We need some timeout */ } if (timeout) thread_add_child(thread->master, vrrp_script_child_thread, vscript, pid, timeout * TIMER_HZ); return; } wait_status = THREAD_CHILD_STATUS(thread); if (WIFEXITED(wait_status)) { int status = WEXITSTATUS(wait_status); #ifdef _SCRIPT_DEBUG_ if (do_script_debug) log_message(LOG_INFO, "pid %d exited with status %d", THREAD_CHILD_PID(thread), status); #endif /* Report if status has changed */ if (status != vscript->last_status) log_message(LOG_INFO, "Script `%s` now returning %d", vscript->sname, status); if (status == 0) { /* success */ script_exit_type = "succeeded"; script_success = true; } else { /* failure */ script_exit_type = "failed"; script_success = false; reason = "exited with status"; reason_code = status; } vscript->last_status = status; } else if (WIFSIGNALED(wait_status)) { #ifdef _SCRIPT_DEBUG_ if (do_script_debug) log_message(LOG_INFO, "pid %d exited due to signal %d (%s)", THREAD_CHILD_PID(thread), WTERMSIG(wait_status), strsignal(WTERMSIG(wait_status))); #endif if (vscript->state == SCRIPT_STATE_REQUESTING_TERMINATION && WTERMSIG(wait_status) == SIGTERM) { /* The script terminated due to a SIGTERM, and we sent it a SIGTERM to * terminate the process. Now make sure any children it created have * died too. */ pid = THREAD_CHILD_PID(thread); kill(-pid, SIGKILL); } /* We treat forced termination as a failure */ if ((vscript->state == SCRIPT_STATE_REQUESTING_TERMINATION && WTERMSIG(wait_status) == SIGTERM) || (vscript->state == SCRIPT_STATE_FORCING_TERMINATION && (WTERMSIG(wait_status) == SIGKILL || WTERMSIG(wait_status) == SIGTERM))) script_exit_type = "timed_out"; else { script_exit_type = "failed"; reason = "due to signal"; reason_code = WTERMSIG(wait_status); } script_success = false; } #ifdef _SCRIPT_DEBUG_ else if (do_script_debug) log_message(LOG_INFO, "wait for pid %d exited with exit code 0x%x", THREAD_CHILD_PID(thread), (unsigned)wait_status); #endif if (script_exit_type) { if (script_success) { if (vscript->result < vscript->rise - 1) { vscript->result++; } else if (vscript->result != vscript->rise + vscript->fall - 1) { if (vscript->result < vscript->rise) { /* i.e. == vscript->rise - 1 */ log_message(LOG_INFO, "VRRP_Script(%s) %s", vscript->sname, script_exit_type); update_script_priorities(vscript, true); } vscript->result = vscript->rise + vscript->fall - 1; } } else { if (vscript->result > vscript->rise) { vscript->result--; } else { if (vscript->result == vscript->rise || vscript->init_state == SCRIPT_INIT_STATE_INIT || vscript->init_state == SCRIPT_INIT_STATE_INIT_RELOAD) { if (reason) log_message(LOG_INFO, "VRRP_Script(%s) %s (%s %d)", vscript->sname, script_exit_type, reason, reason_code); else log_message(LOG_INFO, "VRRP_Script(%s) %s", vscript->sname, script_exit_type); update_script_priorities(vscript, false); } vscript->result = 0; } } } vscript->state = SCRIPT_STATE_IDLE; vscript->init_state = SCRIPT_INIT_STATE_DONE; } /* Thread to send gratuitous ARPs when the sending is rate limited */ void vrrp_arp_thread(thread_ref_t thread) { ip_address_t *ip_addr; interface_t *ifp = THREAD_ARG(thread); while (!list_empty(&ifp->garp_delay->garp_list)) { set_time_now(); if (timercmp(&time_now, &ifp->garp_delay->garp_next_time, <)) break; ip_addr = list_first_entry(&ifp->garp_delay->garp_list, ip_address_t, garp_gna_list); send_gratuitous_arp_immediate(ifp, ip_addr); list_del_init(&ip_addr->garp_gna_list); if (--ip_addr->garp_gna_pending) list_add_tail(&ip_addr->garp_gna_list, &ifp->garp_delay->garp_list); } if (!list_empty(&ifp->garp_delay->garp_list)) thread_add_timer(master, vrrp_arp_thread, ifp, timer_long(timer_sub_now(ifp->garp_delay->garp_next_time))); } /* Thread to send gratuitous NDs when the sending is rate limited */ void vrrp_gna_thread(thread_ref_t thread) { ip_address_t *ip_addr; interface_t *ifp = THREAD_ARG(thread); while (!list_empty(&ifp->garp_delay->gna_list)) { set_time_now(); if (timercmp(&time_now, &ifp->garp_delay->gna_next_time, <)) break; ip_addr = list_first_entry(&ifp->garp_delay->gna_list, ip_address_t, garp_gna_list); ndisc_send_unsolicited_na_immediate(ifp, ip_addr); list_del_init(&ip_addr->garp_gna_list); if (--ip_addr->garp_gna_pending) list_add_tail(&ip_addr->garp_gna_list, &ifp->garp_delay->gna_list); } if (!list_empty(&ifp->garp_delay->gna_list)) thread_add_timer(master, vrrp_gna_thread, ifp, timer_long(timer_sub_now(ifp->garp_delay->gna_next_time))); } #ifdef _WITH_DUMP_THREADS_ void dump_threads(void) { FILE *fp; char time_buf[26]; vrrp_t *vrrp; const char *file_name; file_name = make_file_name("thread_dump.dat", "vrrp", global_data->network_namespace, global_data->instance_name); fp = fopen_safe(file_name, "a"); FREE_CONST(file_name); set_time_now(); ctime_r(&time_now.tv_sec, time_buf); fprintf(fp, "\n%.19s.%6.6" PRI_tv_usec ": Thread dump\n", time_buf, time_now.tv_usec); dump_thread_data(master, fp); fprintf(fp, "alloc = %lu\n", master->alloc); fprintf(fp, "\n"); list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { ctime_r(&vrrp->sands.tv_sec, time_buf); fprintf(fp, "VRRP instance %s, sands %.19s.%6.6" PRI_tv_usec ", status %s\n", vrrp->iname, time_buf, vrrp->sands.tv_usec, vrrp->state == VRRP_STATE_INIT ? "INIT" : vrrp->state == VRRP_STATE_BACK ? "BACKUP" : vrrp->state == VRRP_STATE_MAST ? "MASTER" : vrrp->state == VRRP_STATE_FAULT ? "FAULT" : vrrp->state == VRRP_STATE_STOP ? "STOP" : "unknown"); } fclose(fp); } #endif #ifdef THREAD_DUMP void register_vrrp_scheduler_addresses(void) { register_thread_address("vrrp_arp_thread", vrrp_arp_thread); register_thread_address("vrrp_gna_thread", vrrp_gna_thread); register_thread_address("vrrp_dispatcher_init", vrrp_dispatcher_init); register_thread_address("vrrp_gratuitous_arp_thread", vrrp_gratuitous_arp_thread); register_thread_address("vrrp_lower_prio_gratuitous_arp_thread", vrrp_lower_prio_gratuitous_arp_thread); register_thread_address("vrrp_gratuitous_arp_refresh_thread", vrrp_gratuitous_arp_refresh_thread); register_thread_address("vrrp_gratuitous_arp_vmac_update_thread", vrrp_gratuitous_arp_vmac_update_thread); register_thread_address("vrrp_script_child_thread", vrrp_script_child_thread); register_thread_address("vrrp_script_thread", vrrp_script_thread); register_thread_address("vrrp_read_dispatcher_thread", vrrp_read_dispatcher_thread); #ifdef _WITH_BFD_ register_thread_address("vrrp_bfd_thread", vrrp_bfd_thread); #endif } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_snmp.c0000664000175000017500000043102314701312101014600 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: SNMP agent * * Author: Vincent Bernat * * 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, */ /* * To test this code, one can use the following: * * Build keepalived with SNMP support with one or more of the following options ./configure--enable-snmp-vrrp --enable-snmp-checker [or --enable-snmp to enable previous two options] \ --enable-snmp-rfcv2 --enable-snmp-rfcv3 [or --enable-snmp-rfc to enable previous two options] * Edit /etc/snmp/snmpd.conf to contain the following: rocommunity public rwcommunity private master agentx # agentaddress is what clients such as snmpwalk talk over to make SNMP requests and receive responses. # This can be specified as the "LISTENING ADDRESS" option to snmpd, and is the AGENT parameter to most # SNMP client commands. Processes using udp or tcp addresses will need to be in the same network # namespace. # agentaddress udp:localhost:161 # default # agentxsocket is what sub-agents (such as keepalived) communicate via with snmp. This is what keepalived # needs to match with its snmp_socket parameter, and can be specified to snmpd with the -x option. # agentxsocket unix:/var/agentx/master # default trapcommunity public trap2sink localhost:162 * Edit /etc/snmp/snmptrapd.conf to uncomment the following line: authCommunity log,execute,net public * Put the MIB definition files in a place that will be found: cp doc/[VK]*-MIB.txt /usr/share/snmp/mibs * or cp doc/[VK]*-MIB.txt ~/.snmp/mibs * Run snmpd (in background) snmpd -LS0-6d [-x agentxsocket] [AGENT] If running in a network namespace, run snmpd, snmptrapd etc in that network namespace. To specify a different port/socket to listen on, see above in sample snmpd.conf * Run snmptrapd (in foreground) MIBS="+KEEPALIVED-MIB:VRRP-MIB:VRRPV3-MIB" snmptrapd -f -Lo * or if MIB files copied to ~/.snmp/mibs MIBS="+KEEPALIVED-MIB:VRRP-MIB:VRRPV3-MIB" snmptrapd -f -M "+$HOME/.snmp/mibs" -Lo * Enable SNMP in config file, by adding some or all of the following, depending on which configure options were chosen enable_snmp (enables enable_snmp_vrrp and enable_snmp_checker) enable_snmp_vrrp enable_snmp_checker enable_snmp_rfc (enables enable_snmp_rfcv2 enable_snmp_rfcv3) enable_snmp_rfcv2 enable_snmp_rfcv3 enable_traps * Run keepalived. Some traps/notifications should be generated which will be displayed on the terminal running snmptrapd * To see the MIB trees, where localhost can be replaced by an appropriate AGENT, run (in the same namespace as snmpd) MIBS="+KEEPALIVED-MIB" snmpwalk -v2c -c public localhost KEEPALIVED-MIB::keepalived or MIBS="+VRRP-MIB" snmpwalk -v2c -c public localhost VRRP-MIB::vrrpMIB or MIBS="+VRRPV3-MIB" snmpwalk -v2c -c public localhost VRRPV3-MIB::vrrpv3MIB * To check the validity of a MIB file: smilint doc/KEEPALIVED-MIB.txt * Multiple instances of keepalived cannot register the same MIB with the same instance of snmpd. In order for snmpd to work with multiple instances of keepalived, there would need to be one instance of snmpd per keepalived instance. Using unix domain sockets will not work for this, unless each instance of snmpd is configured to use a different socket, so use network domain sockets e.g. to udp:localhost:705 which will enable keepalived to communicate with its own instance of snmpd running in the same network namespace, and then set snmp_socket in the keepalived global configuration. To run snmpd use snmpd -LS0-6d -x udp:localhost:705, which it appears should work but it doesn't seem to. */ #include "config.h" #if HAVE_DECL_RTA_PREF #include #endif #if HAVE_DECL_RTA_ENCAP #include #endif #include #include #include #include "vrrp.h" #include "vrrp_snmp.h" #include "vrrp_track.h" #include "vrrp_ipaddress.h" #include "vrrp_iproute.h" #include "vrrp_iprule.h" #include "vrrp_scheduler.h" #include "track_file.h" #include "config.h" #include "logger.h" #include "global_data.h" #include "bitops.h" #include "main.h" #include "rttables.h" #include "parser.h" #include "warnings.h" #include "utils.h" #include "snmp.h" #ifdef _WITH_SNMP_VRRP_ /* VRRP SNMP defines */ #define VRRP_OID KEEPALIVED_OID, 2 enum snmp_vrrp_magic { VRRP_SNMP_SCRIPT_NAME = 3, VRRP_SNMP_SCRIPT_COMMAND, VRRP_SNMP_SCRIPT_INTERVAL, VRRP_SNMP_SCRIPT_WEIGHT, VRRP_SNMP_SCRIPT_WEIGHT_REVERSE, VRRP_SNMP_SCRIPT_RESULT, VRRP_SNMP_SCRIPT_RISE, VRRP_SNMP_SCRIPT_FALL, VRRP_SNMP_SCRIPT_PATH, VRRP_SNMP_SCRIPT_INTERVAL_USEC, VRRP_SNMP_SCRIPT_TIMEOUT_USEC, VRRP_SNMP_FILE_NAME, VRRP_SNMP_FILE_PATH, VRRP_SNMP_FILE_RESULT, VRRP_SNMP_FILE_WEIGHT, VRRP_SNMP_FILE_WEIGHT_REVERSE, VRRP_SNMP_BFD_NAME, VRRP_SNMP_BFD_RESULT, VRRP_SNMP_BFD_WEIGHT, VRRP_SNMP_BFD_WEIGHT_REVERSE, VRRP_SNMP_PROCESS_NAME, VRRP_SNMP_PROCESS_PATH, VRRP_SNMP_PROCESS_PARAMS, VRRP_SNMP_PROCESS_PARAM_MATCH, VRRP_SNMP_PROCESS_WEIGHT, VRRP_SNMP_PROCESS_WEIGHT_REVERSE, VRRP_SNMP_PROCESS_QUORUM, VRRP_SNMP_PROCESS_QUORUM_MAX, VRRP_SNMP_PROCESS_FORKDELAY, VRRP_SNMP_PROCESS_TERMINATEDELAY, VRRP_SNMP_PROCESS_FULLCOMMAND, VRRP_SNMP_PROCESS_CURPROC, VRRP_SNMP_PROCESS_RESULT, VRRP_SNMP_ADDRESS_ADDRESSTYPE, VRRP_SNMP_ADDRESS_VALUE, VRRP_SNMP_ADDRESS_BROADCAST, VRRP_SNMP_ADDRESS_MASK, VRRP_SNMP_ADDRESS_SCOPE, VRRP_SNMP_ADDRESS_IFINDEX, VRRP_SNMP_ADDRESS_IFNAME, VRRP_SNMP_ADDRESS_IFALIAS, VRRP_SNMP_ADDRESS_ISSET, VRRP_SNMP_ADDRESS_ISADVERTISED, VRRP_SNMP_ADDRESS_PEER, VRRP_SNMP_SYNCGROUP_NAME, VRRP_SNMP_SYNCGROUP_STATE, VRRP_SNMP_SYNCGROUP_TRACKINGWEIGHT, VRRP_SNMP_SYNCGROUP_SMTPALERT, VRRP_SNMP_SYNCGROUP_NOTIFYEXEC, VRRP_SNMP_SYNCGROUP_SCRIPTMASTER, VRRP_SNMP_SYNCGROUP_SCRIPTBACKUP, VRRP_SNMP_SYNCGROUP_SCRIPTFAULT, VRRP_SNMP_SYNCGROUP_SCRIPTSTOP, VRRP_SNMP_SYNCGROUP_SCRIPT, VRRP_SNMP_SYNCGROUPMEMBER_INSTANCE, VRRP_SNMP_SYNCGROUPMEMBER_NAME, VRRP_SNMP_INSTANCE_NAME, VRRP_SNMP_INSTANCE_VIRTUALROUTERID, VRRP_SNMP_INSTANCE_STATE, VRRP_SNMP_INSTANCE_INITIALSTATE, VRRP_SNMP_INSTANCE_WANTEDSTATE, VRRP_SNMP_INSTANCE_BASEPRIORITY, VRRP_SNMP_INSTANCE_EFFECTIVEPRIORITY, VRRP_SNMP_INSTANCE_VIPSENABLED, VRRP_SNMP_INSTANCE_PRIMARYINTERFACE, VRRP_SNMP_INSTANCE_TRACKPRIMARYIF, VRRP_SNMP_INSTANCE_ADVERTISEMENTSINT, VRRP_SNMP_INSTANCE_PREEMPT, VRRP_SNMP_INSTANCE_PREEMPTDELAY, VRRP_SNMP_INSTANCE_AUTHTYPE, VRRP_SNMP_INSTANCE_USELVSSYNCDAEMON, VRRP_SNMP_INSTANCE_LVSSYNCINTERFACE, VRRP_SNMP_INSTANCE_SYNCGROUP, VRRP_SNMP_INSTANCE_GARPDELAY, VRRP_SNMP_INSTANCE_SMTPALERT, VRRP_SNMP_INSTANCE_NOTIFYEXEC, VRRP_SNMP_INSTANCE_SCRIPTMASTER, VRRP_SNMP_INSTANCE_SCRIPTBACKUP, VRRP_SNMP_INSTANCE_SCRIPTFAULT, VRRP_SNMP_INSTANCE_SCRIPTSTOP, VRRP_SNMP_INSTANCE_SCRIPT, VRRP_SNMP_INSTANCE_ACCEPT, VRRP_SNMP_INSTANCE_PROMOTE_SECONDARIES, VRRP_SNMP_INSTANCE_USE_LINKBEAT, VRRP_SNMP_INSTANCE_VRRP_VERSION, VRRP_SNMP_INSTANCE_SCRIPTMASTER_RX_LOWER_PRI, VRRP_SNMP_INSTANCE_SCRIPTDELETED, VRRP_SNMP_INSTANCE_NOTIFY_DELETED, VRRP_SNMP_INSTANCE_MULTICAST_ADDRESSTYPE, VRRP_SNMP_INSTANCE_MULTICAST_ADDRESS, VRRP_SNMP_INSTANCE_V3_CHECKSUM_AS_V2, VRRP_SNMP_TRACKEDINTERFACE_NAME, VRRP_SNMP_TRACKEDINTERFACE_WEIGHT, VRRP_SNMP_TRACKEDINTERFACE_WEIGHT_REVERSE, VRRP_SNMP_TRACKEDSCRIPT_NAME, VRRP_SNMP_TRACKEDSCRIPT_WEIGHT, VRRP_SNMP_TRACKEDSCRIPT_WEIGHT_REVERSE, VRRP_SNMP_TRACKEDFILE_NAME, VRRP_SNMP_TRACKEDFILE_WEIGHT, VRRP_SNMP_TRACKEDFILE_WEIGHT_REVERSE, VRRP_SNMP_TRACKEDBFD_NAME, VRRP_SNMP_TRACKEDBFD_WEIGHT, VRRP_SNMP_TRACKEDBFD_WEIGHT_REVERSE, VRRP_SNMP_TRACKEDPROCESS_NAME, VRRP_SNMP_TRACKEDPROCESS_WEIGHT, VRRP_SNMP_TRACKEDPROCESS_WEIGHT_REVERSE, VRRP_SNMP_SGROUPTRACKEDINTERFACE_NAME, VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT, VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT_REVERSE, VRRP_SNMP_SGROUPTRACKEDSCRIPT_NAME, VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT, VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT_REVERSE, VRRP_SNMP_SGROUPTRACKEDFILE_NAME, VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT, VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT_REVERSE, VRRP_SNMP_SGROUPTRACKEDBFD_NAME, VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT, VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT_REVERSE, VRRP_SNMP_SGROUPTRACKEDPROCESS_NAME, VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT, VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT_REVERSE, }; enum snmp_rule_magic { VRRP_SNMP_RULE_DIRECTION = 2, VRRP_SNMP_RULE_ADDRESSTYPE, VRRP_SNMP_RULE_ADDRESS, VRRP_SNMP_RULE_ADDRESSMASK, VRRP_SNMP_RULE_ROUTINGTABLE, VRRP_SNMP_RULE_ISSET, VRRP_SNMP_RULE_INVERT, VRRP_SNMP_RULE_DESTINATIONADDRESSTYPE, VRRP_SNMP_RULE_DESTINATIONADDRESS, VRRP_SNMP_RULE_DESTINATIONADDRESSMASK, VRRP_SNMP_RULE_SOURCEADDRESSTYPE, VRRP_SNMP_RULE_SOURCEADDRESS, VRRP_SNMP_RULE_SOURCEADDRESSMASK, VRRP_SNMP_RULE_TOS, VRRP_SNMP_RULE_FWMARK, VRRP_SNMP_RULE_FWMASK, VRRP_SNMP_RULE_REALM_DST, VRRP_SNMP_RULE_REALM_SRC, VRRP_SNMP_RULE_ININTERFACE, VRRP_SNMP_RULE_OUTINTERFACE, VRRP_SNMP_RULE_TARGET, VRRP_SNMP_RULE_ACTION, VRRP_SNMP_RULE_TABLE_NO, VRRP_SNMP_RULE_PREFERENCE, VRRP_SNMP_RULE_SUPPRESSPREFIXLEN, VRRP_SNMP_RULE_SUPPRESSGROUP, VRRP_SNMP_RULE_TUNNELID_HIGH, VRRP_SNMP_RULE_TUNNELID_LOW, VRRP_SNMP_RULE_UID_RANGE_START, VRRP_SNMP_RULE_UID_RANGE_END, VRRP_SNMP_RULE_L3MDEV, VRRP_SNMP_RULE_PROTOCOL, VRRP_SNMP_RULE_IP_PROTO, VRRP_SNMP_RULE_SRC_PORT_START, VRRP_SNMP_RULE_SRC_PORT_END, VRRP_SNMP_RULE_DST_PORT_START, VRRP_SNMP_RULE_DST_PORT_END, }; enum snmp_route_magic { VRRP_SNMP_ROUTE_ADDRESSTYPE = 2, VRRP_SNMP_ROUTE_DESTINATION, VRRP_SNMP_ROUTE_DESTINATIONMASK, VRRP_SNMP_ROUTE_GATEWAY, VRRP_SNMP_ROUTE_SECONDARYGATEWAY, VRRP_SNMP_ROUTE_SOURCE, VRRP_SNMP_ROUTE_METRIC, VRRP_SNMP_ROUTE_SCOPE, VRRP_SNMP_ROUTE_TYPE, VRRP_SNMP_ROUTE_IFINDEX, VRRP_SNMP_ROUTE_IFNAME, VRRP_SNMP_ROUTE_ROUTINGTABLE, VRRP_SNMP_ROUTE_ISSET, VRRP_SNMP_ROUTE_FROM_ADDRESS, VRRP_SNMP_ROUTE_FROM_ADDRESS_MASK, VRRP_SNMP_ROUTE_TOS, VRRP_SNMP_ROUTE_PROTOCOL, VRRP_SNMP_ROUTE_ECN, VRRP_SNMP_ROUTE_QUICK_ACK, VRRP_SNMP_ROUTE_EXPIRES, VRRP_SNMP_ROUTE_MTU, VRRP_SNMP_ROUTE_MTU_LOCK, VRRP_SNMP_ROUTE_HOP_LIMIT, VRRP_SNMP_ROUTE_ADVMSS, VRRP_SNMP_ROUTE_ADVMSS_LOCK, VRRP_SNMP_ROUTE_RTT, VRRP_SNMP_ROUTE_RTT_LOCK, VRRP_SNMP_ROUTE_RTTVAR, VRRP_SNMP_ROUTE_RTTVAR_LOCK, VRRP_SNMP_ROUTE_REORDERING, VRRP_SNMP_ROUTE_REORDERING_LOCK, VRRP_SNMP_ROUTE_WINDOW, VRRP_SNMP_ROUTE_CWND, VRRP_SNMP_ROUTE_CWND_LOCK, VRRP_SNMP_ROUTE_SSTHRESH, VRRP_SNMP_ROUTE_SSTHRESH_LOCK, VRRP_SNMP_ROUTE_RTOMIN, VRRP_SNMP_ROUTE_RTOMIN_LOCK, VRRP_SNMP_ROUTE_INIT_CWND, VRRP_SNMP_ROUTE_INIT_RWND, VRRP_SNMP_ROUTE_CONG_CTL, VRRP_SNMP_ROUTE_PREF, VRRP_SNMP_ROUTE_REALM_DST, VRRP_SNMP_ROUTE_REALM_SRC, VRRP_SNMP_ROUTE_ENCAP_TYPE, VRRP_SNMP_ROUTE_ENCAP_MPLS_LABELS, VRRP_SNMP_ROUTE_ENCAP_ID, VRRP_SNMP_ROUTE_ENCAP_DST_ADDRESS, VRRP_SNMP_ROUTE_ENCAP_SRC_ADDRESS, VRRP_SNMP_ROUTE_ENCAP_TOS, VRRP_SNMP_ROUTE_ENCAP_TTL, VRRP_SNMP_ROUTE_ENCAP_FLAGS, VRRP_SNMP_ROUTE_ENCAP_ILA_LOCATOR, VRRP_SNMP_ROUTE_FASTOPEN_NO_COOKIE, }; enum snmp_next_hop_magic { VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS_TYPE = 2, VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS, VRRP_SNMP_ROUTE_NEXT_HOP_IF_INDEX, VRRP_SNMP_ROUTE_NEXT_HOP_IF_NAME, VRRP_SNMP_ROUTE_NEXT_HOP_WEIGHT, VRRP_SNMP_ROUTE_NEXT_HOP_ONLINK, VRRP_SNMP_ROUTE_NEXT_HOP_REALM_DST, VRRP_SNMP_ROUTE_NEXT_HOP_REALM_SRC, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TYPE, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_MPLS_LABELS, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_ID, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_DST_ADDRESS, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_SRC_ADDRESS, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TOS, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TTL, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_FLAGS, VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_ILA_LOCATOR, }; enum iter_type { ITER_ADDRESSES, ITER_ROUTES, ITER_RULES }; #endif #ifdef _WITH_SNMP_RFCV2_ /* RFC SNMP defines */ #define VRRP_RFC_OID SNMP_OID_MIB2, 68 #define VRRP_RFC_TRAP_OID VRRP_RFC_OID, 0 /* Magic for RFC MIB functions */ enum rfcv2_snmp_node_magic { VRRP_RFC_SNMP_NODE_VER = 2, VRRP_RFC_SNMP_NOTIF_CNTL }; enum rfcv2_snmp_oper_magic { VRRP_RFC_SNMP_OPER_AUTH_KEY = 3, VRRP_RFC_SNMP_OPER_ADVERT_INT, VRRP_RFC_SNMP_OPER_PREEMPT, VRRP_RFC_SNMP_OPER_VR_UPTIME, VRRP_RFC_SNMP_OPER_PROTO, VRRP_RFC_SNMP_OPER_ROW_STAT, VRRP_RFC_SNMP_OPER_VMAC, VRRP_RFC_SNMP_OPER_STATE, VRRP_RFC_SNMP_OPER_ADM_STATE, VRRP_RFC_SNMP_OPER_PRI, VRRP_RFC_SNMP_OPER_ADDR_CNT, VRRP_RFC_SNMP_OPER_MIP, VRRP_RFC_SNMP_OPER_PIP, VRRP_RFC_SNMP_OPER_AUTH_TYPE }; enum rfcv2_snmp_assoc_ip_magic { VRRP_RFC_SNMP_ASSOC_IP_ADDR_ROW = 3 }; enum rfcv2_snmp_stats_err_magic { VRRP_RFC_SNMP_STATS_CHK_ERR = 2, VRRP_RFC_SNMP_STATS_VER_ERR, VRRP_RFC_SNMP_STATS_VRID_ERR }; enum rfcv2_snmp_stats_magic { VRRP_RFC_SNMP_STATS_MASTER = 2, VRRP_RFC_SNMP_STATS_AUTH_INV, VRRP_RFC_SNMP_STATS_AUTH_MIS, VRRP_RFC_SNMP_STATS_PL_ERR, VRRP_RFC_SNMP_STATS_ADV_RCVD, VRRP_RFC_SNMP_STATS_ADV_INT_ERR, VRRP_RFC_SNMP_STATS_AUTH_FAIL, VRRP_RFC_SNMP_STATS_TTL_ERR, VRRP_RFC_SNMP_STATS_PRI_0_RCVD, VRRP_RFC_SNMP_STATS_PRI_0_SENT, VRRP_RFC_SNMP_STATS_INV_TYPE_RCVD, VRRP_RFC_SNMP_STATS_ADDR_LIST_ERR }; /* VRRP_RFC_SNMP_CNFRM_MIB, VRRP_RFC_SNMP_GRP_OPER, VRRP_RFC_SNMP_GRP_STATS, VRRP_RFC_SNMP_GRP_TRAP, VRRP_RFC_SNMP_GRP_NOTIF */ #endif #ifdef _WITH_SNMP_RFCV3_ /* RFC SNMP defines */ #define VRRP_RFCv3_OID SNMP_OID_MIB2, 207 #define VRRP_RFCv3_NOTIFY_OID VRRP_RFCv3_OID, 0 /* Magic for RFC MIB functions */ enum rfcv3_snmp_oper_magic { VRRP_RFCv3_SNMP_OPER_MIP, VRRP_RFCv3_SNMP_OPER_PIP, VRRP_RFCv3_SNMP_OPER_VMAC, VRRP_RFCv3_SNMP_OPER_STATE, VRRP_RFCv3_SNMP_OPER_PRI, VRRP_RFCv3_SNMP_OPER_ADDR_CNT, VRRP_RFCv3_SNMP_OPER_ADVERT_INT, VRRP_RFCv3_SNMP_OPER_PREEMPT, VRRP_RFCv3_SNMP_OPER_ACCEPT, VRRP_RFCv3_SNMP_OPER_VR_UPTIME, VRRP_RFCv3_SNMP_OPER_ROW_STATUS }; enum rfcv3_snmp_assoc_ip_magic { VRRP_RFCv3_SNMP_ASSOC_IP_ADDR_ROW_STATUS = 3 }; enum rfcv3_snmp_stats_err_magic { VRRP_RFCv3_SNMP_STATS_CHK_ERR = 2, VRRP_RFCv3_SNMP_STATS_VER_ERR, VRRP_RFCv3_SNMP_STATS_VRID_ERR, VRRP_RFCv3_SNMP_STATS_DISC_TIME }; enum rfcv3_snmp_stats_magic { VRRP_RFCv3_SNMP_STATS_MASTER = 2, VRRP_RFCv3_SNMP_STATS_MASTER_REASON, VRRP_RFCv3_SNMP_STATS_ADV_RCVD, VRRP_RFCv3_SNMP_STATS_ADV_INT_ERR, VRRP_RFCv3_SNMP_STATS_TTL_ERR, VRRP_RFCv3_SNMP_STATS_PROTO_ERR_REASON, VRRP_RFCv3_SNMP_STATS_PRI_0_RCVD, VRRP_RFCv3_SNMP_STATS_PRI_0_SENT, VRRP_RFCv3_SNMP_STATS_INV_TYPE_RCVD, VRRP_RFCv3_SNMP_STATS_ADDR_LIST_ERR, VRRP_RFCv3_SNMP_STATS_PL_ERR, VRRP_RFCv3_SNMP_STATS_ROW_DISC_TIME, VRRP_RFCv3_SNMP_STATS_REFRESH_RATE }; /* VRRP_RFCv3_SNMP_CNFRM_MIB, VRRP_RFCv3_SNMP_GRP_OPER, VRRP_RFCv3_SNMP_GRP_STATS, VRRP_RFCv3_SNMP_GRP_TRAP, VRRP_RFCv3_SNMP_GRP_NOTIF */ #endif /* Static return value */ static longret_t long_ret; #ifdef _WITH_SNMP_VRRP_ static char buf[MAXBUF]; #endif /* global variable */ #ifdef _WITH_SNMP_RFC_ timeval_t snmp_vrrp_start_time; #endif /* For some reason net-snmp doesn't use a uint64_t for 64 bit counters, but rather uses * a struct, with the high word at the lower address, so we need to assign values according. */ inline static void set_counter64 (struct counter64 *c64, uint64_t val) { c64->high = val >> 32; c64->low = val & 0xffffffff; } #ifdef _FOR_DEBUGGING_ static void sprint_oid(char *str, const oid* oid, int len) { int offs = 0; int i; if (!len) { str[0] = '.'; str[1] = 0; return; } for (i = 0; i < len; i++) offs += sprintf(str + offs, ".%lu", oid[i]); } #endif #ifdef _WITH_SNMP_VRRP_ /* Convert VRRP state to SNMP state */ static int vrrp_snmp_state(int state) { return state <= VRRP_STATE_FAULT ? state : 4; } static u_char* vrrp_snmp_script(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_script_t *scr; list_head_t *e; snmp_ret_t ret; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_script)) == NULL) return NULL; scr = list_entry(e, vrrp_script_t, e_list); switch (vp->magic) { case VRRP_SNMP_SCRIPT_NAME: *var_len = strlen(scr->sname); ret.cp = scr->sname; return ret.p; case VRRP_SNMP_SCRIPT_COMMAND: cmd_str_r(&scr->script, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); case VRRP_SNMP_SCRIPT_INTERVAL: long_ret.u = scr->interval / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SCRIPT_WEIGHT: long_ret.s = scr->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SCRIPT_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(scr->weight_reverse); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SCRIPT_RESULT: switch (scr->init_state) { case SCRIPT_INIT_STATE_INIT: case SCRIPT_INIT_STATE_INIT_RELOAD: long_ret.u = 1; break; case SCRIPT_INIT_STATE_FAILED: long_ret.u = 5; break; default: long_ret.u = (scr->result >= scr->rise) ? 3 : 2; } return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SCRIPT_RISE: long_ret.s = scr->rise; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SCRIPT_FALL: long_ret.s = scr->fall; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SCRIPT_PATH: ret.cp = scr->script.path ? scr->script.path : scr->script.args[0]; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_SCRIPT_INTERVAL_USEC: long_ret.u = scr->interval; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SCRIPT_TIMEOUT_USEC: long_ret.u = scr->timeout; return PTR_CAST(u_char, &long_ret); default: break; } return NULL; } static u_char* vrrp_snmp_file(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { tracked_file_t *file; list_head_t *e; snmp_ret_t ret; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_track_files)) == NULL) return NULL; file = list_entry(e, tracked_file_t, e_list); switch (vp->magic) { case VRRP_SNMP_FILE_NAME: *var_len = strlen(file->fname); ret.cp = file->fname; return ret.p; case VRRP_SNMP_FILE_PATH: *var_len = strlen(file->file_path); ret.cp = file->file_path; return ret.p; case VRRP_SNMP_FILE_RESULT: long_ret.s = file->last_status; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_FILE_WEIGHT: long_ret.s = file->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_FILE_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(file->weight_reverse); return PTR_CAST(u_char, &long_ret); default: break; } return NULL; } #ifdef _WITH_BFD_ static u_char* vrrp_snmp_bfd(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_tracked_bfd_t *bfd; list_head_t *e; snmp_ret_t ret; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_track_bfds)) == NULL) return NULL; bfd = list_entry(e, vrrp_tracked_bfd_t, e_list); switch (vp->magic) { case VRRP_SNMP_BFD_NAME: *var_len = strlen(bfd->bname); ret.cp = bfd->bname; return ret.p; case VRRP_SNMP_BFD_RESULT: long_ret.s = bfd->bfd_up; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_BFD_WEIGHT: long_ret.s = bfd->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_BFD_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bfd->weight_reverse); return PTR_CAST(u_char, &long_ret); default: break; } return NULL; } #endif #ifdef _WITH_TRACK_PROCESS_ static u_char* vrrp_snmp_process(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_tracked_process_t *proc; list_head_t *e; snmp_ret_t ret; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_track_processes)) == NULL) return NULL; proc = list_entry(e, vrrp_tracked_process_t, e_list); switch (vp->magic) { case VRRP_SNMP_PROCESS_NAME: *var_len = strlen(proc->pname); ret.cp = proc->pname; return ret.p; case VRRP_SNMP_PROCESS_PATH: *var_len = strlen(proc->process_path); ret.cp = proc->process_path; return ret.p; case VRRP_SNMP_PROCESS_PARAMS: if (!proc->process_params_len) { *var_len = 0; ret.cp = ""; } else { /* We need to replace the nul terminators with spaces */ size_t len; unsigned i; len = proc->process_params_len - 1 < sizeof(buf) ? proc->process_params_len - 1 : sizeof(buf); memcpy(buf, proc->process_params, len); buf[sizeof(buf) - 1] = '\0'; for (i = strlen(buf); i < len; i += strlen(buf + i)) { buf[i++] = ' '; if (i >= len) break; } *var_len = len; ret.cp = buf; } return ret.p; case VRRP_SNMP_PROCESS_PARAM_MATCH: long_ret.u = proc->param_match; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_WEIGHT: long_ret.u = proc->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(proc->weight_reverse); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_QUORUM: long_ret.u = proc->quorum; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_QUORUM_MAX: long_ret.u = proc->quorum_max; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_FORKDELAY: long_ret.u = proc->fork_delay; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_TERMINATEDELAY: long_ret.u = proc->terminate_delay; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_FULLCOMMAND: long_ret.u = SNMP_TruthValue(proc->full_command); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_CURPROC: long_ret.u = proc->num_cur_proc; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_PROCESS_RESULT: long_ret.u = SNMP_TruthValue(proc->have_quorum); return PTR_CAST(u_char, &long_ret); default: break; } return NULL; } #endif static list_head_t * vrrp_header_ar_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method, enum iter_type type, int *adv) { oid *target, current[2]; int result; size_t target_len; list_head_t *l2; list_head_t *e; vrrp_t *vrrp; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } *write_method = 0; *var_len = sizeof(unsigned long); /* We search the best match: equal if exact, the lowest OID in the set of the OID strictly superior to the target otherwise. */ target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; l2 = NULL; vrrp = NULL; current[0] = 0; current[1] = 0; while (true) { /* Start with static addresses, then for each vrrp instance the VIPs then the eVIPs */ if (!l2) { if (type == ITER_ADDRESSES) l2 = &vrrp_data->static_addresses; else if (type == ITER_ROUTES) l2 = &vrrp_data->static_routes; else /* if (type == ITER_RULES) */ l2 = &vrrp_data->static_rules; } else if (!vrrp) { if (list_empty(&vrrp_data->vrrp)) break; vrrp = list_first_entry(&vrrp_data->vrrp, vrrp_t, e_list); if (type == ITER_ADDRESSES) l2 = &vrrp->vip; else if (type == ITER_ROUTES) l2 = &vrrp->vroutes; else /* if (type == ITER_RULES) */ l2 = &vrrp->vrules; current[0]++; current[1] = 0; *adv = 1; } else if (type == ITER_ADDRESSES && l2 == &vrrp->vip) { l2 = &vrrp->evip; *adv = 0; } else { /* if ITER_ADDRESSES, l2 == vrrp->evip */ if (list_is_last(&vrrp->e_list, &vrrp_data->vrrp)) break; vrrp = list_entry(vrrp->e_list.next, vrrp_t, e_list); if (type == ITER_ADDRESSES) l2 = &vrrp->vip; else if (type == ITER_ROUTES) l2 = &vrrp->vroutes; else /* if (type == ITER_RULES) */ l2 = &vrrp->vrules; current[0]++; current[1] = 0; } if (target_len && current[0] < target[0]) continue; /* Should not happen... */ if (!l2) return NULL; list_for_each(e, l2) { current[1]++; result = snmp_oid_compare(current, 2, target, target_len); if (result < 0) continue; if (result == 0) { if (!exact) continue; } else if (result > 0 && exact) return NULL; /* This is our best match */ if (!exact) { memcpy(target, current, sizeof(oid) * 2); *length = (unsigned)vp->namelen + 2; } return e; } } return NULL; } #define MAX_PTR ((void*)~0) static nexthop_t * vrrp_header_nh_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { oid *target; int result; size_t target_len; list_head_t *l2; oid curinstance[3]; vrrp_t *vrrp; ip_route_t *route; nexthop_t *nh; bool same; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } *write_method = 0; *var_len = sizeof(unsigned long); /* We search the best match: equal if exact, the lower OID in the set of the OID strictly superior to the target otherwise. */ target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; if (exact && !target_len) return NULL; /* Perform static routes */ for (vrrp = MAX_PTR, curinstance[0] = 0; vrrp; vrrp = ((vrrp == MAX_PTR) ? (list_empty(&vrrp_data->vrrp) ? NULL : list_first_entry(&vrrp_data->vrrp, vrrp_t, e_list)) : list_is_last(&vrrp->e_list, &vrrp_data->vrrp) ? NULL : list_entry(vrrp->e_list.next, vrrp_t, e_list)), curinstance[0]++) { if (exact && curinstance[0] > target[0]) return NULL; if (target_len && curinstance[0] < target[0]) continue; same = (target_len && curinstance[0] == target[0]); l2 = (vrrp == MAX_PTR) ? &vrrp_data->static_routes : &vrrp->vroutes; if (list_empty(l2)) continue; curinstance[1] = 0; list_for_each_entry(route, l2, e_list) { curinstance[1]++; if (exact && curinstance[1] > target[1]) return NULL; if (same && curinstance[1] < target[1]) continue; same = (same && curinstance[1] == target[1]); if (list_empty(&route->nhs)) continue; curinstance[2] = 0; list_for_each_entry(nh, &route->nhs, e_list) { curinstance[2]++; if (exact && target_len && curinstance[2] > target[2]) return NULL; if (same && curinstance[2] < target[2]) continue; if (target_len && !exact && curinstance[0] == target[0] && curinstance[1] == target[1] && curinstance[2] == target[2]) continue; memcpy(target, curinstance, sizeof(oid) * 3); *length = (unsigned)vp->namelen + 3; return nh; } } } return NULL; } static u_char * vrrp_snmp_address(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { ip_address_t *addr; list_head_t *e; int adv = 0; if ((e = vrrp_header_ar_table(vp, name, length, exact, var_len, write_method, ITER_ADDRESSES, &adv)) == NULL) return NULL; addr = list_entry(e, ip_address_t, e_list); switch (vp->magic) { case VRRP_SNMP_ADDRESS_ADDRESSTYPE: long_ret.u = SNMP_InetAddressType(addr->ifa.ifa_family); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ADDRESS_VALUE: if (addr->ifa.ifa_family == AF_INET6) { *var_len = sizeof addr->u.sin6_addr; return PTR_CAST(u_char, &addr->u.sin6_addr); } else { *var_len = sizeof addr->u.sin.sin_addr; return PTR_CAST(u_char, &addr->u.sin.sin_addr); } break; case VRRP_SNMP_ADDRESS_BROADCAST: if (addr->ifa.ifa_family == AF_INET6) break; *var_len = sizeof addr->u.sin.sin_brd; return PTR_CAST(u_char, &addr->u.sin.sin_brd); case VRRP_SNMP_ADDRESS_MASK: long_ret.u = addr->ifa.ifa_prefixlen; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ADDRESS_SCOPE: long_ret.u = snmp_scope(addr->ifa.ifa_scope); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ADDRESS_IFINDEX: long_ret.u = addr->ifp->ifindex; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ADDRESS_IFNAME: *var_len = strlen(addr->ifp->ifname); return PTR_CAST(u_char, addr->ifp->ifname); case VRRP_SNMP_ADDRESS_IFALIAS: if (addr->label) { *var_len = strlen(addr->label); return PTR_CAST(u_char, addr->label); } break; case VRRP_SNMP_ADDRESS_ISSET: long_ret.u = SNMP_TruthValue(addr->set); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ADDRESS_ISADVERTISED: long_ret.u = SNMP_TruthValue(adv); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ADDRESS_PEER: if (!addr->have_peer) break; if (addr->ifa.ifa_family == AF_INET6) { *var_len = sizeof addr->peer.sin6_addr; return PTR_CAST(u_char, &addr->peer.sin6_addr); } else { *var_len = sizeof addr->peer.sin_addr; return PTR_CAST(u_char, &addr->peer.sin_addr); } break; default: return NULL; } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_snmp_address(vp, name, length, exact, var_len, write_method); return NULL; } static u_char* vrrp_snmp_route(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { ip_route_t *route; list_head_t *e; nexthop_t *gw2; int gw2_cnt = 0, adv; if ((e = vrrp_header_ar_table(vp, name, length, exact, var_len, write_method, ITER_ROUTES, &adv)) == NULL) return NULL; route = list_entry(e, ip_route_t, e_list); switch (vp->magic) { case VRRP_SNMP_ROUTE_ADDRESSTYPE: long_ret.u = AF_INET; /* IPv4 only */ if (route->dst) long_ret.u = route->dst->ifa.ifa_family; else if (route->src) long_ret.u = route->src->ifa.ifa_family; else if (route->pref_src) long_ret.u = route->pref_src->ifa.ifa_family; if (long_ret.u == AF_INET6) long_ret.u = 2; else long_ret.u = 1; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_DESTINATION: if (!route->dst) break; if (route->dst->ifa.ifa_family == AF_INET6) { *var_len = sizeof route->dst->u.sin6_addr; return PTR_CAST(u_char, &route->dst->u.sin6_addr); } *var_len = sizeof route->dst->u.sin.sin_addr; return PTR_CAST(u_char, &route->dst->u.sin.sin_addr); case VRRP_SNMP_ROUTE_DESTINATIONMASK: if (!route->dst) break; long_ret.u = route->dst->ifa.ifa_prefixlen; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_GATEWAY: if (!route->via) break; if (route->via->ifa.ifa_family == AF_INET6) { *var_len = sizeof route->via->u.sin6_addr; return PTR_CAST(u_char, &route->via->u.sin6_addr); } *var_len = sizeof route->via->u.sin.sin_addr; return PTR_CAST(u_char, &route->via->u.sin.sin_addr); case VRRP_SNMP_ROUTE_SECONDARYGATEWAY: if (list_empty(&route->nhs)) break; list_for_each_entry(gw2, &route->nhs, e_list) gw2_cnt++; if (gw2_cnt != 1) break; gw2 = list_first_entry(&route->nhs, nexthop_t, e_list); #if HAVE_DECL_RTA_ENCAP if (gw2->encap.type != LWTUNNEL_ENCAP_NONE) break; #endif if (gw2->addr->ifa.ifa_family == AF_INET6) { *var_len = sizeof gw2->addr->u.sin6_addr; return PTR_CAST(u_char, &gw2->addr->u.sin6_addr); } *var_len = sizeof gw2->addr->u.sin.sin_addr; return PTR_CAST(u_char, &gw2->addr->u.sin.sin_addr); case VRRP_SNMP_ROUTE_SOURCE: if (!route->pref_src) break; if (route->pref_src->ifa.ifa_family == AF_INET6) { *var_len = sizeof route->pref_src->u.sin6_addr; return PTR_CAST(u_char, &route->pref_src->u.sin6_addr); } *var_len = sizeof route->pref_src->u.sin.sin_addr; return PTR_CAST(u_char, &route->pref_src->u.sin.sin_addr); case VRRP_SNMP_ROUTE_METRIC: long_ret.u = route->metric; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_SCOPE: long_ret.u = snmp_scope(route->scope); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_TYPE: if (!list_empty(&route->nhs)) long_ret.u = 2; else if (route->type == RTN_BLACKHOLE) long_ret.u = 3; else if (route->type == RTN_ANYCAST) long_ret.u = 4; else if (route->type == RTN_MULTICAST) long_ret.u = 5; else if (route->type == RTN_BROADCAST) long_ret.u = 6; else if (route->type == RTN_UNREACHABLE) long_ret.u = 7; else if (route->type == RTN_PROHIBIT) long_ret.u = 8; else if (route->type == RTN_THROW) long_ret.u = 9; else if (route->type == RTN_NAT) long_ret.u = 10; else if (route->type == RTN_XRESOLVE) long_ret.u = 11; else long_ret.u = 1; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_IFINDEX: if (!route->oif) break; long_ret.u = route->oif->ifindex; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_IFNAME: if (!route->oif) break; *var_len = strlen(IF_NAME(route->oif)); return PTR_CAST(u_char, &IF_NAME(route->oif)); case VRRP_SNMP_ROUTE_ROUTINGTABLE: long_ret.u = route->table; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_ISSET: long_ret.u = SNMP_TruthValue(route->set); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_FROM_ADDRESS: if (!route->src) break; if (route->src->ifa.ifa_family == AF_INET6) { *var_len = sizeof route->src->u.sin6_addr; return PTR_CAST(u_char, &route->src->u.sin6_addr); } else { *var_len = sizeof route->src->u.sin.sin_addr; return PTR_CAST(u_char, &route->src->u.sin.sin_addr); } case VRRP_SNMP_ROUTE_FROM_ADDRESS_MASK: if (!route->src) break; long_ret.u = route->src->ifa.ifa_prefixlen; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_TOS: if (!(route->mask & IPROUTE_BIT_DSFIELD)) break; long_ret.u = route->tos; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_PROTOCOL: if (!(route->mask & IPROUTE_BIT_PROTOCOL)) break; long_ret.s = route->protocol + 1; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_ECN: long_ret.u = SNMP_TruthValue(route->features & RTAX_FEATURE_ECN); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_QUICK_ACK: long_ret.u = SNMP_TruthValue(route->mask & IPROUTE_BIT_QUICKACK); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_EXPIRES: #if !HAVE_DECL_RTA_EXPIRES break; #else if (!(route->mask & IPROUTE_BIT_EXPIRES)) break; long_ret.u = route->expires; return PTR_CAST(u_char, &long_ret); #endif case VRRP_SNMP_ROUTE_MTU: if (!(route->mask & IPROUTE_BIT_MTU)) break; long_ret.u = route->mtu; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_MTU_LOCK: if (!(route->mask & IPROUTE_BIT_MTU)) break; long_ret.u = SNMP_TruthValue(route->lock & (1<mask & IPROUTE_BIT_HOPLIMIT)) break; long_ret.u = route->hoplimit; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_ADVMSS: if (!(route->mask & IPROUTE_BIT_HOPLIMIT)) break; long_ret.u = route->advmss; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_ADVMSS_LOCK: if (!(route->mask & IPROUTE_BIT_ADVMSS)) break; long_ret.u = SNMP_TruthValue(route->lock & (1<mask & IPROUTE_BIT_RTT)) break; long_ret.u = route->rtt / 8; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_RTT_LOCK: if (!(route->mask & IPROUTE_BIT_RTT)) break; long_ret.u = SNMP_TruthValue(route->lock & (1<mask & IPROUTE_BIT_RTTVAR)) break; long_ret.u = route->rttvar / 4; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_RTTVAR_LOCK: if (!(route->mask & IPROUTE_BIT_RTTVAR)) break; long_ret.u = SNMP_TruthValue(route->lock & (1<mask & IPROUTE_BIT_REORDERING)) break; long_ret.u = route->reordering; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_REORDERING_LOCK: if (!(route->mask & IPROUTE_BIT_REORDERING)) break; long_ret.u = SNMP_TruthValue(route->lock & (1<mask & IPROUTE_BIT_WINDOW)) break; long_ret.u = route->window; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_CWND: if (!(route->mask & IPROUTE_BIT_CWND)) break; long_ret.u = route->cwnd; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_CWND_LOCK: if (!(route->mask & IPROUTE_BIT_CWND)) break; long_ret.u = SNMP_TruthValue(route->lock & (1<mask & IPROUTE_BIT_SSTHRESH)) break; long_ret.u = route->ssthresh; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_SSTHRESH_LOCK: if (!(route->mask & IPROUTE_BIT_SSTHRESH)) break; long_ret.u = SNMP_TruthValue(route->lock & (1<mask & IPROUTE_BIT_RTO_MIN)) break; long_ret.u = route->rto_min; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_RTOMIN_LOCK: if (!(route->mask & IPROUTE_BIT_RTO_MIN)) break; long_ret.u = SNMP_TruthValue(route->lock & (1<mask & IPROUTE_BIT_INITCWND)) break; long_ret.u = route->initcwnd; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_INIT_RWND: if (!(route->mask & IPROUTE_BIT_INITRWND)) break; long_ret.u = route->initrwnd; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_CONG_CTL: #if !HAVE_DECL_RTAX_CC_ALGO break; #else if (!route->congctl) break; *var_len = strlen(route->congctl); return PTR_CAST(u_char, route->congctl); #endif case VRRP_SNMP_ROUTE_PREF: #if !HAVE_DECL_RTA_PREF break; #else if (!(route->mask & IPROUTE_BIT_PREF)) break; long_ret.u = route->pref == ICMPV6_ROUTER_PREF_LOW ? 1 : route->pref == ICMPV6_ROUTER_PREF_MEDIUM ? 2 : route->pref == ICMPV6_ROUTER_PREF_HIGH ? 3 : 0; return PTR_CAST(u_char, &long_ret); #endif case VRRP_SNMP_ROUTE_FASTOPEN_NO_COOKIE: #if !HAVE_DECL_RTAX_FASTOPEN_NO_COOKIE break; #else if (!(route->mask & IPROUTE_BIT_FASTOPEN_NO_COOKIE)) break; long_ret.u = SNMP_TruthValue(route->fastopen_no_cookie); return PTR_CAST(u_char, &long_ret); #endif case VRRP_SNMP_ROUTE_REALM_DST: if (!route->realms) break; long_ret.u = route->realms & 0xFFFF; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_REALM_SRC: if (!(route->realms & 0xFFFF0000)) break; long_ret.u = route->realms >> 16; return PTR_CAST(u_char, &long_ret); default: return NULL; } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_snmp_route(vp, name, length, exact, var_len, write_method); return NULL; } #if HAVE_DECL_RTA_ENCAP static u_char* vrrp_snmp_encap(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { ip_route_t *route; list_head_t *e; nexthop_t *nh; encap_t *encap; int adv = 0; static struct counter64 c64; #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS static char labels[11*MAX_MPLS_LABELS]; char *op; unsigned i; #endif if (vp->name[vp->namelen - 3] == 7) { if ((e = vrrp_header_ar_table(vp, name, length, exact, var_len, write_method, ITER_ROUTES, &adv)) == NULL) return NULL; route = list_entry(e, ip_route_t, e_list); encap = &route->encap; } else { if ((nh = vrrp_header_nh_table(vp, name, length, exact, var_len, write_method)) == NULL) return NULL; encap = &nh->encap; } // TODO - enable this to work for main route or next hop - following in a separate function callable by both if (encap->type != LWTUNNEL_ENCAP_NONE) { if (vp->magic == VRRP_SNMP_ROUTE_ENCAP_TYPE) { long_ret.s = encap->type + 1; return PTR_CAST(u_char, &long_ret); } if (encap->type == LWTUNNEL_ENCAP_IP || encap->type == LWTUNNEL_ENCAP_IP6) { switch(vp->magic) { case VRRP_SNMP_ROUTE_ENCAP_ID: if (!(encap->flags & IPROUTE_BIT_ENCAP_ID)) break; *var_len = sizeof(c64); set_counter64 (&c64, encap->ip.id); return PTR_CAST(u_char, &c64); case VRRP_SNMP_ROUTE_ENCAP_DST_ADDRESS: if (!encap->ip.dst) break; if (encap->ip.dst->ifa.ifa_family == AF_INET6) { *var_len = sizeof(encap->ip.dst->u.sin6_addr); return PTR_CAST(u_char, &encap->ip.dst->u.sin6_addr); } *var_len = sizeof(encap->ip.dst->u.sin.sin_addr.s_addr); return PTR_CAST(u_char, &encap->ip.dst->u.sin.sin_addr.s_addr); case VRRP_SNMP_ROUTE_ENCAP_SRC_ADDRESS: if (!encap->ip.src) break; if (encap->ip.src->ifa.ifa_family == AF_INET6) { *var_len = sizeof(encap->ip.src->u.sin6_addr); return PTR_CAST(u_char, &encap->ip.src->u.sin6_addr); } *var_len = sizeof(encap->ip.src->u.sin.sin_addr.s_addr); return PTR_CAST(u_char, &encap->ip.src->u.sin.sin_addr.s_addr); case VRRP_SNMP_ROUTE_ENCAP_TOS: if (!(encap->flags & IPROUTE_BIT_ENCAP_DSFIELD)) break; long_ret.u = encap->ip.tos; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_ENCAP_TTL: if (!(encap->flags & IPROUTE_BIT_ENCAP_TTL)) break; long_ret.u = encap->ip.ttl; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_ENCAP_FLAGS: if (!(encap->flags & IPROUTE_BIT_ENCAP_FLAGS)) break; long_ret.u = encap->ip.flags; return PTR_CAST(u_char, &long_ret); } } #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS else if (encap->type == LWTUNNEL_ENCAP_MPLS) { if (vp->magic == VRRP_SNMP_ROUTE_ENCAP_MPLS_LABELS) { op = labels; for (i = 0; i < encap->mpls.num_labels; i++) op += snprintf(op, (size_t)(labels + sizeof(labels) - op), "%s%u", i ? "/" : "", encap->mpls.addr[i].entry); *var_len = strlen(labels); return PTR_CAST(u_char, labels); } } #endif #if HAVE_DECL_LWTUNNEL_ENCAP_ILA else if (encap->type == LWTUNNEL_ENCAP_ILA) { if (vp->magic == VRRP_SNMP_ROUTE_ENCAP_ILA_LOCATOR) { *var_len = sizeof(c64); set_counter64 (&c64, encap->ila.locator); return PTR_CAST(u_char, &c64); } } #endif } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_snmp_encap(vp, name, length, exact, var_len, write_method); return NULL; } #endif static u_char* vrrp_snmp_next_hop(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { nexthop_t *nh; if ((nh = vrrp_header_nh_table(vp, name, length, exact, var_len, write_method)) == NULL) return NULL; switch (vp->magic) { case VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS_TYPE: if (!nh->addr) break; long_ret.u = SNMP_InetAddressType(nh->addr->ifa.ifa_family); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS: if (!nh->addr) break; if (nh->addr->ifa.ifa_family == AF_INET6) { *var_len = sizeof nh->addr->u.sin6_addr; return PTR_CAST(u_char, &nh->addr->u.sin6_addr); } *var_len = sizeof nh->addr->u.sin.sin_addr; return PTR_CAST(u_char, &nh->addr->u.sin.sin_addr); case VRRP_SNMP_ROUTE_NEXT_HOP_IF_INDEX: if (!nh->ifp) break; long_ret.u = nh->ifp->ifindex; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_NEXT_HOP_IF_NAME: if (!nh->ifp) break; *var_len = strlen(nh->ifp->ifname); return PTR_CAST(u_char, &nh->ifp->ifname); case VRRP_SNMP_ROUTE_NEXT_HOP_WEIGHT: if (!(nh->mask & IPROUTE_BIT_WEIGHT)) break; long_ret.s = nh->weight + 1; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_NEXT_HOP_ONLINK: long_ret.u = SNMP_TruthValue(nh->flags & RTNH_F_ONLINK); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_NEXT_HOP_REALM_DST: if (!nh->realms) break; long_ret.u = nh->realms & 0xFFFF; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_ROUTE_NEXT_HOP_REALM_SRC: if (!(nh->realms & 0xFFFF0000)) break; long_ret.u = nh->realms >> 16; return PTR_CAST(u_char, &long_ret); default: break; } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_snmp_next_hop(vp, name, length, exact, var_len, write_method); return NULL; } static u_char* vrrp_snmp_rule(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { ip_rule_t *rule; list_head_t *e; int adv = 0; const char *str; ip_address_t *addr; if ((e = vrrp_header_ar_table(vp, name, length, exact, var_len, write_method, ITER_RULES, &adv)) == NULL) return NULL; rule = list_entry(e, ip_rule_t, e_list); switch (vp->magic) { case VRRP_SNMP_RULE_DIRECTION: /* obsolete */ str = rule->to_addr ? rule->from_addr ? "both" : "to" : rule->from_addr ? "from" : ""; *var_len = strlen(str); return PTR_CAST(u_char, no_const_char_p(str)); case VRRP_SNMP_RULE_ADDRESSTYPE: /* obsolete */ addr = rule->to_addr ? rule->to_addr : rule->from_addr; if (!addr) break; long_ret.u = SNMP_InetAddressType(addr->ifa.ifa_family); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_ADDRESS: /* obsolete */ addr = rule->to_addr ? rule->to_addr : rule->from_addr; if (!addr) break; if (addr->ifa.ifa_family == AF_INET6) { *var_len = sizeof(addr->u.sin6_addr); return PTR_CAST(u_char, &addr->u.sin6_addr); } *var_len = sizeof(addr->u.sin.sin_addr); return PTR_CAST(u_char, &addr->u.sin.sin_addr); case VRRP_SNMP_RULE_ADDRESSMASK: /* obsolete */ addr = rule->to_addr ? rule->to_addr : rule->from_addr; if (!addr) break; long_ret.u = addr->ifa.ifa_prefixlen; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_ROUTINGTABLE: if (rule->action != FR_ACT_TO_TBL) break; long_ret.u = rule->table; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_ISSET: long_ret.u = SNMP_TruthValue(rule->set); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_INVERT: long_ret.u = SNMP_TruthValue(rule->invert); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_DESTINATIONADDRESSTYPE: if (!rule->to_addr) break; long_ret.u = SNMP_InetAddressType(rule->to_addr->ifa.ifa_family); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_DESTINATIONADDRESS: if (!rule->to_addr) break; if (rule->to_addr->ifa.ifa_family == AF_INET6) { *var_len = sizeof(rule->to_addr->u.sin6_addr); return PTR_CAST(u_char, &rule->to_addr->u.sin6_addr); } *var_len = sizeof(rule->to_addr->u.sin.sin_addr); return PTR_CAST(u_char, &rule->to_addr->u.sin.sin_addr); case VRRP_SNMP_RULE_DESTINATIONADDRESSMASK: if (!rule->to_addr) break; long_ret.u = rule->to_addr->ifa.ifa_prefixlen; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_SOURCEADDRESSTYPE: if (!rule->from_addr) break; long_ret.u = SNMP_InetAddressType(rule->from_addr->ifa.ifa_family); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_SOURCEADDRESS: if (!rule->from_addr) break; if (rule->from_addr->ifa.ifa_family == AF_INET6) { *var_len = sizeof(rule->from_addr->u.sin6_addr); return PTR_CAST(u_char, &rule->from_addr->u.sin6_addr); } *var_len = sizeof(rule->from_addr->u.sin.sin_addr); return PTR_CAST(u_char, &rule->from_addr->u.sin.sin_addr); case VRRP_SNMP_RULE_SOURCEADDRESSMASK: if (!rule->from_addr) break; long_ret.u = rule->from_addr->ifa.ifa_prefixlen; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_TOS: long_ret.u = rule->tos; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_FWMARK: if (rule->mask & IPRULE_BIT_FWMARK) long_ret.u = rule->fwmark; else break; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_FWMASK: if (rule->mask & IPRULE_BIT_FWMASK) long_ret.u = rule->fwmask; else break; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_REALM_DST: if (!rule->realms) break; long_ret.u = rule->realms & 0xFFFF; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_REALM_SRC: if (!(rule->realms & 0xFFFF0000)) break; long_ret.u = rule->realms >> 16; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_ININTERFACE: if (!rule->iif) break; *var_len = strlen(rule->iif->ifname); return PTR_CAST(u_char, rule->iif->ifname); case VRRP_SNMP_RULE_OUTINTERFACE: if (!rule->oif) break; *var_len = strlen(rule->oif->ifname); return PTR_CAST(u_char, rule->oif->ifname); case VRRP_SNMP_RULE_TARGET: if (!(rule->action == FR_ACT_GOTO)) break; long_ret.u = rule->goto_target; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_ACTION: long_ret.u = rule->action; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_TABLE_NO: if (rule->action != FR_ACT_TO_TBL) break; long_ret.u = rule->table; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_PREFERENCE: if (!rule->priority) break; long_ret.u = rule->priority; return PTR_CAST(u_char, &long_ret); #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN case VRRP_SNMP_RULE_SUPPRESSPREFIXLEN: if (rule->suppress_prefix_len != -1) long_ret.u = rule->suppress_prefix_len; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_SUPPRESS_IFGROUP case VRRP_SNMP_RULE_SUPPRESSGROUP: if (rule->mask & IPRULE_BIT_SUP_GROUP) { RELAX_CAST_QUAL_START str = PTR_CAST_CONST(char, get_rttables_group(rule->suppress_group)); RELAX_CAST_QUAL_END *var_len = strlen(str); } else break; return PTR_CAST(u_char, no_const_char_p(str)); #endif #if HAVE_DECL_FRA_TUN_ID case VRRP_SNMP_RULE_TUNNELID_HIGH: if (rule->tunnel_id) long_ret.u = rule->tunnel_id >> 32; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_TUN_ID case VRRP_SNMP_RULE_TUNNELID_LOW: if (rule->tunnel_id) long_ret.u = rule->tunnel_id & 0xffffffff; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_UID_RANGE case VRRP_SNMP_RULE_UID_RANGE_START: if (rule->mask & IPRULE_BIT_UID_RANGE) long_ret.u = rule->uid_range.start; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_UID_RANGE case VRRP_SNMP_RULE_UID_RANGE_END: if (rule->mask & IPRULE_BIT_UID_RANGE) long_ret.u = rule->uid_range.end; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_L3MDEV case VRRP_SNMP_RULE_L3MDEV: if (rule->l3mdev) long_ret.u = 1; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_PROTOCOL case VRRP_SNMP_RULE_PROTOCOL: if (rule->mask & IPRULE_BIT_PROTOCOL) long_ret.u = rule->protocol; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_IP_PROTO case VRRP_SNMP_RULE_IP_PROTO: if (rule->mask & IPRULE_BIT_IP_PROTO) long_ret.u = rule->ip_proto; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_SPORT_RANGE case VRRP_SNMP_RULE_SRC_PORT_START: if (rule->mask & IPRULE_BIT_SPORT_RANGE) long_ret.u = rule->src_port.start; else break; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_SRC_PORT_END: if (rule->mask & IPRULE_BIT_SPORT_RANGE) long_ret.u = rule->src_port.end; else break; return PTR_CAST(u_char, &long_ret); #endif #if HAVE_DECL_FRA_DPORT_RANGE case VRRP_SNMP_RULE_DST_PORT_START: if (rule->mask & IPRULE_BIT_DPORT_RANGE) long_ret.u = rule->dst_port.start; else break; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_RULE_DST_PORT_END: if (rule->mask & IPRULE_BIT_DPORT_RANGE) long_ret.u = rule->dst_port.end; else break; return PTR_CAST(u_char, &long_ret); #endif default: return NULL; } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_snmp_rule(vp, name, length, exact, var_len, write_method); return NULL; } static u_char * vrrp_snmp_syncgroup(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_sgroup_t *group; list_head_t *e; snmp_ret_t ret; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_sync_group)) == NULL) return NULL; group = list_entry(e, vrrp_sgroup_t, e_list); switch (vp->magic) { case VRRP_SNMP_SYNCGROUP_NAME: *var_len = strlen(group->gname); ret.cp = group->gname; return ret.p; case VRRP_SNMP_SYNCGROUP_STATE: long_ret.s = vrrp_snmp_state(group->state); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SYNCGROUP_TRACKINGWEIGHT: long_ret.u = SNMP_TruthValue(group->sgroup_tracking_weight); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SYNCGROUP_SMTPALERT: long_ret.u = SNMP_TruthValue(group->smtp_alert); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SYNCGROUP_NOTIFYEXEC: long_ret.u = SNMP_TruthValue(group->notify_exec); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SYNCGROUP_SCRIPTMASTER: if (group->script_master) { cmd_str_r(group->script_master, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_SYNCGROUP_SCRIPTBACKUP: if (group->script_backup) { cmd_str_r(group->script_backup, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_SYNCGROUP_SCRIPTFAULT: if (group->script_fault) { cmd_str_r(group->script_fault, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_SYNCGROUP_SCRIPTSTOP: if (group->script_stop) { cmd_str_r(group->script_stop, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_SYNCGROUP_SCRIPT: if (group->script) { cmd_str_r(group->script, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; default: return NULL; } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_snmp_syncgroup(vp, name, length, exact, var_len, write_method); return NULL; } static u_char * vrrp_snmp_syncgroupmember(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { snmp_ret_t ret; vrrp_t *vrrp; list_head_t *e; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_sync_group, offsetof(vrrp_sgroup_t, e_list), offsetof(vrrp_sgroup_t, vrrp_instances)); if (!e) return NULL; vrrp = list_entry(e, vrrp_t, s_list); ret.cp = vrrp->iname; *var_len = strlen(ret.cp); return ret.p; } static vrrp_t * _get_instance(oid *name, size_t name_len) { oid instance; vrrp_t *vrrp = NULL; if (name_len < 1) return NULL; instance = name[name_len - 1]; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (--instance == 0) break; } return vrrp; } #ifdef _WITH_FIREWALL_ static int #ifndef ALLOW_SNMP_SET_ACCEPT vrrp_snmp_instance_accept(__attribute__((unused)) int action, __attribute__((unused)) u_char *var_val, __attribute__((unused)) u_char var_val_type, __attribute__((unused)) size_t var_val_len, __attribute__((unused)) u_char *statP, __attribute__((unused)) oid *name, __attribute__((unused)) size_t name_len) { return SNMP_ERR_NOACCESS; } #else vrrp_snmp_instance_accept(int action, u_char *var_val, u_char var_val_type, size_t var_val_len, __attribute__((unused)) u_char *statP, oid *name, size_t name_len) { vrrp_t *vrrp = NULL; switch (action) { case RESERVE1: /* Check that the proposed value is acceptable */ if (var_val_type != ASN_INTEGER) return SNMP_ERR_WRONGTYPE; if (var_val_len > sizeof(long)) return SNMP_ERR_WRONGLENGTH; switch ((long)(*var_val)) { // TODO - We must check that we are not address owner (especially if disabling) case 1: /* enable accept */ case 2: /* disable accept */ break; default: return SNMP_ERR_WRONGVALUE; } break; case RESERVE2: /* Check that we can find the instance.*/ case COMMIT: /* Find the instance */ vrrp = _get_instance(name, name_len); if (!vrrp) return SNMP_ERR_NOSUCHNAME; if (action == RESERVE2) break; /* Commit: change values. There is no way to fail. */ switch ((long)(*var_val)) { case 1: log_message(LOG_INFO, "(%s) accept mode enabled with SNMP", vrrp->iname); // TODO - What do we do about adding/removing iptables blocks? // RFC6527 requires the instance to be down to change this - can't find now where it says that vrrp->accept = true; break; case 2: log_message(LOG_INFO, "(%s) accept mode disabled with SNMP", vrrp->iname); vrrp->accept = false; break; } break; } return SNMP_ERR_NOERROR; } #endif #endif static int vrrp_snmp_instance_priority(int action, u_char *var_val, u_char var_val_type, size_t var_val_len, __attribute__((unused)) u_char *statP, oid *name, size_t name_len) { vrrp_t *vrrp = NULL; switch (action) { case RESERVE1: /* Check that the proposed priority is acceptable */ if (var_val_type != ASN_INTEGER) return SNMP_ERR_WRONGTYPE; if (var_val_len > sizeof(long)) return SNMP_ERR_WRONGLENGTH; if (*var_val == 0) return SNMP_ERR_WRONGVALUE; break; case RESERVE2: /* Check that we can find the instance. We should. */ case COMMIT: /* Find the instance */ vrrp = _get_instance(name, name_len); if (!vrrp) return SNMP_ERR_NOSUCHNAME; if (action == RESERVE2) break; /* Commit: change values. There is no way to fail. */ log_message(LOG_INFO, "(%s) base priority changed from" " %u to %u via SNMP.", vrrp->iname, vrrp->base_priority, *var_val); vrrp->total_priority += *var_val - vrrp->base_priority; vrrp->base_priority = *var_val; vrrp_set_effective_priority(vrrp); //TODO - could affect accept break; } return SNMP_ERR_NOERROR; } static int vrrp_snmp_instance_preempt(int action, u_char *var_val, u_char var_val_type, size_t var_val_len, __attribute__((unused)) u_char *statP, oid *name, size_t name_len) { vrrp_t *vrrp = NULL; switch (action) { case RESERVE1: /* Check that the proposed value is acceptable */ if (var_val_type != ASN_INTEGER) return SNMP_ERR_WRONGTYPE; if (var_val_len > sizeof(long)) return SNMP_ERR_WRONGLENGTH; switch ((long)(*var_val)) { case 1: /* enable preemption */ case 2: /* disable preemption */ break; default: return SNMP_ERR_WRONGVALUE; } break; case RESERVE2: /* Check that we can find the instance. We should. */ case COMMIT: /* Find the instance */ vrrp = _get_instance(name, name_len); if (!vrrp) return SNMP_ERR_NOSUCHNAME; if (action == RESERVE2) break; /* Commit: change values. There is no way to fail. */ switch ((long)(*var_val)) { case 1: log_message(LOG_INFO, "(%s) preemption enabled with SNMP", vrrp->iname); __clear_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags); break; case 2: log_message(LOG_INFO, "(%s) preemption disabled with SNMP", vrrp->iname); __set_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags); break; } break; } return SNMP_ERR_NOERROR; } static u_char* vrrp_snmp_instance(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { snmp_ret_t ret; list_head_t *e; vrrp_t *rt; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp)) == NULL) return NULL; rt = list_entry(e, vrrp_t, e_list); switch (vp->magic) { case VRRP_SNMP_INSTANCE_NAME: *var_len = strlen(rt->iname); ret.cp = rt->iname; return ret.p; case VRRP_SNMP_INSTANCE_VIRTUALROUTERID: long_ret.u = rt->vrid; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_STATE: long_ret.s = vrrp_snmp_state(rt->state); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_INITIALSTATE: long_ret.s = vrrp_snmp_state(rt->configured_state); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_WANTEDSTATE: long_ret.s = vrrp_snmp_state(rt->wantstate); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_BASEPRIORITY: long_ret.u = rt->base_priority; *write_method = vrrp_snmp_instance_priority; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_EFFECTIVEPRIORITY: long_ret.u = rt->effective_priority; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_VIPSENABLED: long_ret.u = SNMP_TruthValue(rt->vipset); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_PRIMARYINTERFACE: if (!rt->ifp) return NULL; *var_len = strlen(rt->ifp->ifname); return PTR_CAST(u_char, &rt->ifp->ifname); case VRRP_SNMP_INSTANCE_TRACKPRIMARYIF: long_ret.u = SNMP_TruthValue(!list_empty(&rt->track_ifp)); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_ADVERTISEMENTSINT: long_ret.u = (rt->version == VRRP_VERSION_2) ? rt->adver_int / TIMER_HZ : rt->adver_int / TIMER_CENTI_HZ; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_PREEMPT: long_ret.u = SNMP_TruthValue(!__test_bit(VRRP_FLAG_NOPREEMPT, &rt->flags)); *write_method = vrrp_snmp_instance_preempt; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_PREEMPTDELAY: long_ret.u = rt->preempt_delay / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_AUTHTYPE: long_ret.u = 0; if (rt->version == VRRP_VERSION_2) { #ifdef _WITH_VRRP_AUTH_ long_ret.u = rt->auth_type; #endif } return PTR_CAST(u_char, &long_ret); #ifdef _WITH_LVS_ case VRRP_SNMP_INSTANCE_USELVSSYNCDAEMON: long_ret.u = SNMP_TruthValue(global_data->lvs_syncd.vrrp == rt); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_LVSSYNCINTERFACE: if (global_data->lvs_syncd.vrrp == rt) { *var_len = strlen(global_data->lvs_syncd.ifname); ret.cp = global_data->lvs_syncd.ifname; return ret.p; } break; #endif case VRRP_SNMP_INSTANCE_SYNCGROUP: if (rt->sync) { *var_len = strlen(rt->sync->gname); ret.cp = rt->sync->gname; return ret.p; } break; case VRRP_SNMP_INSTANCE_GARPDELAY: long_ret.u = rt->garp_delay / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_SMTPALERT: long_ret.u = SNMP_TruthValue(rt->smtp_alert); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_NOTIFYEXEC: long_ret.u = SNMP_TruthValue(rt->notify_exec); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_SCRIPTMASTER: if (rt->script_master) { cmd_str_r(rt->script_master, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_INSTANCE_SCRIPTBACKUP: if (rt->script_backup) { cmd_str_r(rt->script_backup, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_INSTANCE_SCRIPTFAULT: if (rt->script_fault) { cmd_str_r(rt->script_fault, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_INSTANCE_SCRIPTSTOP: if (rt->script_stop) { cmd_str_r(rt->script_stop, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_INSTANCE_SCRIPTDELETED: if (rt->script_deleted) { cmd_str_r(rt->script_deleted, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_INSTANCE_SCRIPTMASTER_RX_LOWER_PRI: if (rt->script_master_rx_lower_pri) { cmd_str_r(rt->script_master_rx_lower_pri, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_INSTANCE_SCRIPT: if (rt->script) { cmd_str_r(rt->script, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); } break; case VRRP_SNMP_INSTANCE_ACCEPT: long_ret.u = 0; #ifdef _WITH_FIREWALL_ if (rt->version == VRRP_VERSION_3) { long_ret.u = SNMP_TruthValue(rt->accept); *write_method = vrrp_snmp_instance_accept; } #endif return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_PROMOTE_SECONDARIES: long_ret.u = SNMP_TruthValue(__test_bit(VRRP_FLAG_PROMOTE_SECONDARIES, &rt->flags)); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_USE_LINKBEAT: long_ret.u = SNMP_TruthValue(__test_bit(VRRP_FLAG_LINKBEAT_USE_POLLING, &rt->flags)); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_VRRP_VERSION: long_ret.u = rt->version; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_NOTIFY_DELETED: long_ret.u = SNMP_TruthValue(rt->notify_deleted); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_MULTICAST_ADDRESSTYPE: if (__test_bit(VRRP_FLAG_UNICAST, &rt->flags)) break; long_ret.u = SNMP_InetAddressType(rt->mcast_daddr.ss_family); return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_INSTANCE_MULTICAST_ADDRESS: if (__test_bit(VRRP_FLAG_UNICAST, &rt->flags)) break; if (rt->mcast_daddr.ss_family == AF_INET6) { *var_len = sizeof PTR_CAST(struct sockaddr_in6, &rt->mcast_daddr)->sin6_addr; return PTR_CAST(u_char, &PTR_CAST(struct sockaddr_in6, &rt->mcast_daddr)->sin6_addr); } else { *var_len = sizeof PTR_CAST(struct sockaddr_in, &rt->mcast_daddr)->sin_addr; return PTR_CAST(u_char, &PTR_CAST(struct sockaddr_in, &rt->mcast_daddr)->sin_addr); } break; case VRRP_SNMP_INSTANCE_V3_CHECKSUM_AS_V2: long_ret.u = (__test_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, &rt->flags)) ? 1 : 2; return PTR_CAST(u_char, &long_ret); default: return NULL; } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_snmp_instance(vp, name, length, exact, var_len, write_method); return NULL; } static u_char * vrrp_snmp_trackedinterface(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_if_t *bifp; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp, offsetof(vrrp_t, e_list), offsetof(vrrp_t, track_ifp)); if (!e) return NULL; bifp = list_entry(e, tracked_if_t, e_list); switch (vp->magic) { case VRRP_SNMP_TRACKEDINTERFACE_NAME: ret.cp = bifp->ifp->ifname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_TRACKEDINTERFACE_WEIGHT: long_ret.s = bifp->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_TRACKEDINTERFACE_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bifp->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } static u_char* vrrp_snmp_trackedscript(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_sc_t *bscr; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp, offsetof(vrrp_t, e_list), offsetof(vrrp_t, track_script)); if (!e) return NULL; bscr = list_entry(e, tracked_sc_t, e_list); switch (vp->magic) { case VRRP_SNMP_TRACKEDSCRIPT_NAME: ret.cp = bscr->scr->sname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_TRACKEDSCRIPT_WEIGHT: long_ret.s = bscr->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_TRACKEDSCRIPT_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bscr->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } static u_char* vrrp_snmp_trackedfile(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_file_monitor_t *bfile; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp, offsetof(vrrp_t, e_list), offsetof(vrrp_t, track_file)); if (!e) return NULL; bfile = list_entry(e, tracked_file_monitor_t, e_list); switch(vp->magic) { case VRRP_SNMP_TRACKEDFILE_NAME: ret.cp = bfile->file->fname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_TRACKEDFILE_WEIGHT: long_ret.s = bfile->file->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_TRACKEDFILE_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bfile->file->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } #ifdef _WITH_BFD_ static u_char* vrrp_snmp_trackedbfd(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_bfd_t *bbfd; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp, offsetof(vrrp_t, e_list), offsetof(vrrp_t, track_bfd)); if (!e) return NULL; bbfd = list_entry(e, tracked_bfd_t, e_list); switch(vp->magic) { case VRRP_SNMP_TRACKEDBFD_NAME: ret.cp = bbfd->bfd->bname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_TRACKEDBFD_WEIGHT: long_ret.s = bbfd->bfd->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_TRACKEDBFD_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bbfd->bfd->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } #endif #ifdef _WITH_TRACK_PROCESS_ static u_char* vrrp_snmp_trackedprocess(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_process_t *bproc; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp, offsetof(vrrp_t, e_list), offsetof(vrrp_t, track_process)); if (!e) return NULL; bproc = list_entry(e, tracked_process_t, e_list); switch(vp->magic) { case VRRP_SNMP_TRACKEDPROCESS_NAME: ret.cp = bproc->process->pname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_TRACKEDPROCESS_WEIGHT: long_ret.s = bproc->process->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_TRACKEDPROCESS_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bproc->process->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } #endif static u_char* vrrp_snmp_group_trackedinterface(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_if_t *bifp; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_sync_group, offsetof(vrrp_sgroup_t, e_list), offsetof(vrrp_sgroup_t, track_ifp)); if (!e) return NULL; bifp = list_entry(e, tracked_if_t, e_list); switch (vp->magic) { case VRRP_SNMP_SGROUPTRACKEDINTERFACE_NAME: ret.cp = bifp->ifp->ifname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT: long_ret.s = bifp->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bifp->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } static u_char* vrrp_snmp_group_trackedscript(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_sc_t *bscr; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_sync_group, offsetof(vrrp_sgroup_t, e_list), offsetof(vrrp_sgroup_t, track_script)); if (!e) return NULL; bscr = list_entry(e, tracked_sc_t, e_list); switch (vp->magic) { case VRRP_SNMP_SGROUPTRACKEDSCRIPT_NAME: ret.cp = bscr->scr->sname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT: long_ret.s = bscr->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bscr->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } static u_char* vrrp_snmp_group_trackedfile(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_file_monitor_t *bfile; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_sync_group, offsetof(vrrp_sgroup_t, e_list), offsetof(vrrp_sgroup_t, track_file)); if (!e) return NULL; bfile = list_entry(e, tracked_file_monitor_t, e_list); switch(vp->magic) { case VRRP_SNMP_SGROUPTRACKEDFILE_NAME: ret.cp = bfile->file->fname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT: long_ret.s = bfile->file->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bfile->file->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } #ifdef _WITH_BFD_ static u_char* vrrp_snmp_group_trackedbfd(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_bfd_t *bbfd; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_sync_group, offsetof(vrrp_sgroup_t, e_list), offsetof(vrrp_sgroup_t, track_bfd)); if (!e) return NULL; bbfd = list_entry(e, tracked_bfd_t, e_list); switch(vp->magic) { case VRRP_SNMP_SGROUPTRACKEDBFD_NAME: ret.cp = bbfd->bfd->bname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT: long_ret.s = bbfd->bfd->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bbfd->bfd->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } #endif #ifdef _WITH_TRACK_PROCESS_ static u_char* vrrp_snmp_group_trackedprocess(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { const tracked_process_t *bproc; list_head_t *e; snmp_ret_t ret; e = snmp_find_element(vp, name, length, exact, var_len, write_method, &vrrp_data->vrrp_sync_group, offsetof(vrrp_sgroup_t, e_list), offsetof(vrrp_sgroup_t, track_process)); if (!e) return NULL; bproc = list_entry(e, tracked_process_t, e_list); switch(vp->magic) { case VRRP_SNMP_SGROUPTRACKEDPROCESS_NAME: ret.cp = bproc->process->pname; *var_len = strlen(ret.cp); return ret.p; case VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT: long_ret.s = bproc->process->weight; return PTR_CAST(u_char, &long_ret); case VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT_REVERSE: long_ret.u = SNMP_TruthValue(bproc->process->weight_reverse); return PTR_CAST(u_char, &long_ret); } return NULL; } #endif static oid vrrp_oid[] = {VRRP_OID}; static struct variable3 vrrp_vars[] = { /* vrrpSyncGroupTable */ {VRRP_SNMP_SYNCGROUP_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 2}}, {VRRP_SNMP_SYNCGROUP_STATE, ASN_INTEGER, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 3}}, {VRRP_SNMP_SYNCGROUP_SMTPALERT, ASN_INTEGER, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 4}}, {VRRP_SNMP_SYNCGROUP_NOTIFYEXEC, ASN_INTEGER, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 5}}, {VRRP_SNMP_SYNCGROUP_SCRIPTMASTER, ASN_OCTET_STR, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 6}}, {VRRP_SNMP_SYNCGROUP_SCRIPTBACKUP, ASN_OCTET_STR, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 7}}, {VRRP_SNMP_SYNCGROUP_SCRIPTFAULT, ASN_OCTET_STR, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 8}}, {VRRP_SNMP_SYNCGROUP_SCRIPT, ASN_OCTET_STR, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 9}}, {VRRP_SNMP_SYNCGROUP_TRACKINGWEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 10}}, {VRRP_SNMP_SYNCGROUP_SCRIPTSTOP, ASN_OCTET_STR, RONLY, vrrp_snmp_syncgroup, 3, {1, 1, 11}}, /* vrrpSyncGroupMemberTable */ {VRRP_SNMP_SYNCGROUPMEMBER_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_syncgroupmember, 3, {2, 1, 2}}, /* vrrpInstanceTable */ {VRRP_SNMP_INSTANCE_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 2}}, {VRRP_SNMP_INSTANCE_VIRTUALROUTERID, ASN_UNSIGNED, RONLY, vrrp_snmp_instance, 3, {3, 1, 3}}, {VRRP_SNMP_INSTANCE_STATE, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 4}}, {VRRP_SNMP_INSTANCE_INITIALSTATE, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 5}}, {VRRP_SNMP_INSTANCE_WANTEDSTATE, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 6}}, {VRRP_SNMP_INSTANCE_BASEPRIORITY, ASN_INTEGER, RWRITE, vrrp_snmp_instance, 3, {3, 1, 7}}, {VRRP_SNMP_INSTANCE_EFFECTIVEPRIORITY, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 8}}, {VRRP_SNMP_INSTANCE_VIPSENABLED, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 9}}, {VRRP_SNMP_INSTANCE_PRIMARYINTERFACE, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 10}}, {VRRP_SNMP_INSTANCE_TRACKPRIMARYIF, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 11}}, {VRRP_SNMP_INSTANCE_ADVERTISEMENTSINT, ASN_UNSIGNED, RONLY, vrrp_snmp_instance, 3, {3, 1, 12}}, {VRRP_SNMP_INSTANCE_PREEMPT, ASN_INTEGER, RWRITE, vrrp_snmp_instance, 3, {3, 1, 13}}, {VRRP_SNMP_INSTANCE_PREEMPTDELAY, ASN_UNSIGNED, RONLY, vrrp_snmp_instance, 3, {3, 1, 14}}, {VRRP_SNMP_INSTANCE_AUTHTYPE, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 15}}, {VRRP_SNMP_INSTANCE_USELVSSYNCDAEMON, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 16}}, {VRRP_SNMP_INSTANCE_LVSSYNCINTERFACE, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 17}}, {VRRP_SNMP_INSTANCE_SYNCGROUP, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 18}}, {VRRP_SNMP_INSTANCE_GARPDELAY, ASN_UNSIGNED, RONLY, vrrp_snmp_instance, 3, {3, 1, 19}}, {VRRP_SNMP_INSTANCE_SMTPALERT, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 20}}, {VRRP_SNMP_INSTANCE_NOTIFYEXEC, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 21}}, {VRRP_SNMP_INSTANCE_SCRIPTMASTER, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 22}}, {VRRP_SNMP_INSTANCE_SCRIPTBACKUP, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 23}}, {VRRP_SNMP_INSTANCE_SCRIPTFAULT, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 24}}, {VRRP_SNMP_INSTANCE_SCRIPTSTOP, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 25}}, {VRRP_SNMP_INSTANCE_SCRIPT, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 26}}, {VRRP_SNMP_INSTANCE_ACCEPT, ASN_INTEGER, RWRITE, vrrp_snmp_instance, 3, {3, 1, 27}}, {VRRP_SNMP_INSTANCE_PROMOTE_SECONDARIES, ASN_INTEGER, RWRITE, vrrp_snmp_instance, 3, {3, 1, 28}}, {VRRP_SNMP_INSTANCE_USE_LINKBEAT, ASN_INTEGER, RWRITE, vrrp_snmp_instance, 3, {3, 1, 29}}, {VRRP_SNMP_INSTANCE_VRRP_VERSION, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 30}}, {VRRP_SNMP_INSTANCE_SCRIPTMASTER_RX_LOWER_PRI, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 31}}, {VRRP_SNMP_INSTANCE_SCRIPTDELETED, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 32}}, {VRRP_SNMP_INSTANCE_NOTIFY_DELETED, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 33}}, {VRRP_SNMP_INSTANCE_MULTICAST_ADDRESSTYPE, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 34}}, {VRRP_SNMP_INSTANCE_MULTICAST_ADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_instance, 3, {3, 1, 35}}, {VRRP_SNMP_INSTANCE_V3_CHECKSUM_AS_V2, ASN_INTEGER, RONLY, vrrp_snmp_instance, 3, {3, 1, 36}}, /* vrrpTrackedInterfaceTable */ {VRRP_SNMP_TRACKEDINTERFACE_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_trackedinterface, 3, {4, 1, 1}}, {VRRP_SNMP_TRACKEDINTERFACE_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_trackedinterface, 3, {4, 1, 2}}, {VRRP_SNMP_TRACKEDINTERFACE_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_trackedinterface, 3, {4, 1, 3}}, /* vrrpTrackedScriptTable */ {VRRP_SNMP_TRACKEDSCRIPT_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_trackedscript, 3, {5, 1, 2}}, {VRRP_SNMP_TRACKEDSCRIPT_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_trackedscript, 3, {5, 1, 3}}, {VRRP_SNMP_TRACKEDSCRIPT_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_trackedscript, 3, {5, 1, 4}}, /* vrrpAddressTable */ {VRRP_SNMP_ADDRESS_ADDRESSTYPE, ASN_INTEGER, RONLY, vrrp_snmp_address, 3, {6, 1, 2}}, {VRRP_SNMP_ADDRESS_VALUE, ASN_OCTET_STR, RONLY, vrrp_snmp_address, 3, {6, 1, 3}}, {VRRP_SNMP_ADDRESS_BROADCAST, ASN_OCTET_STR, RONLY, vrrp_snmp_address, 3, {6, 1, 4}}, {VRRP_SNMP_ADDRESS_MASK, ASN_UNSIGNED, RONLY, vrrp_snmp_address, 3, {6, 1, 5}}, {VRRP_SNMP_ADDRESS_SCOPE, ASN_INTEGER, RONLY, vrrp_snmp_address, 3, {6, 1, 6}}, {VRRP_SNMP_ADDRESS_IFINDEX, ASN_INTEGER, RONLY, vrrp_snmp_address, 3, {6, 1, 7}}, {VRRP_SNMP_ADDRESS_IFNAME, ASN_OCTET_STR, RONLY, vrrp_snmp_address, 3, {6, 1, 8}}, {VRRP_SNMP_ADDRESS_IFALIAS, ASN_OCTET_STR, RONLY, vrrp_snmp_address, 3, {6, 1, 9}}, {VRRP_SNMP_ADDRESS_ISSET, ASN_INTEGER, RONLY, vrrp_snmp_address, 3, {6, 1, 10}}, {VRRP_SNMP_ADDRESS_ISADVERTISED, ASN_INTEGER, RONLY, vrrp_snmp_address, 3, {6, 1, 11}}, {VRRP_SNMP_ADDRESS_PEER, ASN_OCTET_STR, RONLY, vrrp_snmp_address, 3, {6, 1, 12}}, /* vrrpRouteTable */ {VRRP_SNMP_ROUTE_ADDRESSTYPE, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 2}}, {VRRP_SNMP_ROUTE_DESTINATION, ASN_OCTET_STR, RONLY, vrrp_snmp_route, 3, {7, 1, 3}}, {VRRP_SNMP_ROUTE_DESTINATIONMASK, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 4}}, {VRRP_SNMP_ROUTE_GATEWAY, ASN_OCTET_STR, RONLY, vrrp_snmp_route, 3, {7, 1, 5}}, {VRRP_SNMP_ROUTE_SECONDARYGATEWAY, ASN_OCTET_STR, RONLY, vrrp_snmp_route, 3, {7, 1, 6}}, {VRRP_SNMP_ROUTE_SOURCE, ASN_OCTET_STR, RONLY, vrrp_snmp_route, 3, {7, 1, 7}}, {VRRP_SNMP_ROUTE_METRIC, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 8}}, {VRRP_SNMP_ROUTE_SCOPE, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 9}}, {VRRP_SNMP_ROUTE_TYPE, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 10}}, {VRRP_SNMP_ROUTE_IFINDEX, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 11}}, {VRRP_SNMP_ROUTE_IFNAME, ASN_OCTET_STR, RONLY, vrrp_snmp_route, 3, {7, 1, 12}}, {VRRP_SNMP_ROUTE_ROUTINGTABLE, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 13}}, {VRRP_SNMP_ROUTE_ISSET, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 14}}, {VRRP_SNMP_ROUTE_FROM_ADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_route, 3, {7, 1, 15}}, {VRRP_SNMP_ROUTE_FROM_ADDRESS_MASK, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 16}}, {VRRP_SNMP_ROUTE_TOS, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 17}}, {VRRP_SNMP_ROUTE_PROTOCOL, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 18}}, {VRRP_SNMP_ROUTE_ECN, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 19}}, {VRRP_SNMP_ROUTE_QUICK_ACK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 20}}, {VRRP_SNMP_ROUTE_EXPIRES, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 21}}, {VRRP_SNMP_ROUTE_MTU, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 22}}, {VRRP_SNMP_ROUTE_MTU_LOCK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 23}}, {VRRP_SNMP_ROUTE_HOP_LIMIT, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 24}}, {VRRP_SNMP_ROUTE_ADVMSS, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 25}}, {VRRP_SNMP_ROUTE_ADVMSS_LOCK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 26}}, {VRRP_SNMP_ROUTE_RTT, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 27}}, {VRRP_SNMP_ROUTE_RTT_LOCK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 28}}, {VRRP_SNMP_ROUTE_RTTVAR, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 29}}, {VRRP_SNMP_ROUTE_RTTVAR_LOCK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 30}}, {VRRP_SNMP_ROUTE_REORDERING, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 31}}, {VRRP_SNMP_ROUTE_REORDERING_LOCK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 32}}, {VRRP_SNMP_ROUTE_WINDOW, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 33}}, {VRRP_SNMP_ROUTE_CWND, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 34}}, {VRRP_SNMP_ROUTE_CWND_LOCK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 35}}, {VRRP_SNMP_ROUTE_SSTHRESH, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 36}}, {VRRP_SNMP_ROUTE_SSTHRESH_LOCK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 37}}, {VRRP_SNMP_ROUTE_RTOMIN, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 38}}, {VRRP_SNMP_ROUTE_RTOMIN_LOCK, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 39}}, {VRRP_SNMP_ROUTE_INIT_CWND, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 40}}, {VRRP_SNMP_ROUTE_INIT_RWND, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 41}}, {VRRP_SNMP_ROUTE_CONG_CTL, ASN_OCTET_STR, RONLY, vrrp_snmp_route, 3, {7, 1, 42}}, {VRRP_SNMP_ROUTE_PREF, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 43}}, {VRRP_SNMP_ROUTE_REALM_DST, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 44}}, {VRRP_SNMP_ROUTE_REALM_SRC, ASN_UNSIGNED, RONLY, vrrp_snmp_route, 3, {7, 1, 45}}, #if HAVE_DECL_RTA_ENCAP {VRRP_SNMP_ROUTE_ENCAP_TYPE, ASN_INTEGER, RONLY, vrrp_snmp_encap, 3, {7, 1, 46}}, #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS {VRRP_SNMP_ROUTE_ENCAP_MPLS_LABELS, ASN_OCTET_STR, RONLY, vrrp_snmp_encap, 3, {7, 1, 47}}, #endif {VRRP_SNMP_ROUTE_ENCAP_ID, ASN_COUNTER64, RONLY, vrrp_snmp_encap, 3, {7, 1, 48}}, {VRRP_SNMP_ROUTE_ENCAP_DST_ADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_encap, 3, {7, 1, 49}}, {VRRP_SNMP_ROUTE_ENCAP_SRC_ADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_encap, 3, {7, 1, 50}}, {VRRP_SNMP_ROUTE_ENCAP_TOS, ASN_UNSIGNED, RONLY, vrrp_snmp_encap, 3, {7, 1, 51}}, {VRRP_SNMP_ROUTE_ENCAP_TTL, ASN_UNSIGNED, RONLY, vrrp_snmp_encap, 3, {7, 1, 52}}, {VRRP_SNMP_ROUTE_ENCAP_FLAGS, ASN_UNSIGNED, RONLY, vrrp_snmp_encap, 3, {7, 1, 53}}, #if HAVE_DECL_LWTUNNEL_ENCAP_ILA {VRRP_SNMP_ROUTE_ENCAP_ILA_LOCATOR, ASN_COUNTER64, RONLY, vrrp_snmp_encap, 3, {7, 1, 54}}, #endif #if HAVE_DECL_RTAX_FASTOPEN_NO_COOKIE {VRRP_SNMP_ROUTE_FASTOPEN_NO_COOKIE, ASN_INTEGER, RONLY, vrrp_snmp_route, 3, {7, 1, 55}}, #endif #endif /* vrrpRuleTable */ {VRRP_SNMP_RULE_DIRECTION, ASN_OCTET_STR, RONLY, vrrp_snmp_rule, 3, {8, 1, 2}}, {VRRP_SNMP_RULE_ADDRESSTYPE, ASN_INTEGER, RONLY, vrrp_snmp_rule, 3, {8, 1, 3}}, {VRRP_SNMP_RULE_ADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_rule, 3, {8, 1, 4}}, {VRRP_SNMP_RULE_ADDRESSMASK, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 5}}, {VRRP_SNMP_RULE_ROUTINGTABLE, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 6}}, {VRRP_SNMP_RULE_ISSET, ASN_INTEGER, RONLY, vrrp_snmp_rule, 3, {8, 1, 7}}, {VRRP_SNMP_RULE_INVERT, ASN_INTEGER, RONLY, vrrp_snmp_rule, 3, {8, 1, 8}}, {VRRP_SNMP_RULE_DESTINATIONADDRESSTYPE, ASN_INTEGER, RONLY, vrrp_snmp_rule, 3, {8, 1, 9}}, {VRRP_SNMP_RULE_DESTINATIONADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_rule, 3, {8, 1, 10}}, {VRRP_SNMP_RULE_DESTINATIONADDRESSMASK, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 11}}, {VRRP_SNMP_RULE_SOURCEADDRESSTYPE, ASN_INTEGER, RONLY, vrrp_snmp_rule, 3, {8, 1, 12}}, {VRRP_SNMP_RULE_SOURCEADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_rule, 3, {8, 1, 13}}, {VRRP_SNMP_RULE_SOURCEADDRESSMASK, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 14}}, {VRRP_SNMP_RULE_TOS, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 15}}, {VRRP_SNMP_RULE_FWMARK, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 16}}, {VRRP_SNMP_RULE_FWMASK, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 17}}, {VRRP_SNMP_RULE_REALM_DST, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 18}}, {VRRP_SNMP_RULE_REALM_SRC, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 19}}, {VRRP_SNMP_RULE_ININTERFACE, ASN_OCTET_STR, RONLY, vrrp_snmp_rule, 3, {8, 1, 20}}, {VRRP_SNMP_RULE_OUTINTERFACE, ASN_OCTET_STR, RONLY, vrrp_snmp_rule, 3, {8, 1, 21}}, {VRRP_SNMP_RULE_TARGET, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 22}}, {VRRP_SNMP_RULE_ACTION, ASN_INTEGER, RONLY, vrrp_snmp_rule, 3, {8, 1, 23}}, {VRRP_SNMP_RULE_TABLE_NO, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 24}}, {VRRP_SNMP_RULE_PREFERENCE, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 25}}, {VRRP_SNMP_RULE_SUPPRESSPREFIXLEN, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 26}}, {VRRP_SNMP_RULE_SUPPRESSGROUP, ASN_OCTET_STR, RONLY, vrrp_snmp_rule, 3, {8, 1, 27}}, {VRRP_SNMP_RULE_TUNNELID_HIGH, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 28}}, {VRRP_SNMP_RULE_TUNNELID_LOW, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 29}}, #if HAVE_DECL_FRA_UID_RANGE {VRRP_SNMP_RULE_UID_RANGE_START, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 30}}, {VRRP_SNMP_RULE_UID_RANGE_END, ASN_UNSIGNED, RONLY, vrrp_snmp_rule, 3, {8, 1, 31}}, #endif #if HAVE_DECL_FRA_L3MDEV {VRRP_SNMP_RULE_L3MDEV, ASN_INTEGER, RONLY, vrrp_snmp_rule, 3, {8, 1, 32}}, #endif /* vrrpScriptTable */ {VRRP_SNMP_SCRIPT_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_script, 3, {9, 1, 2}}, {VRRP_SNMP_SCRIPT_COMMAND, ASN_OCTET_STR, RONLY, vrrp_snmp_script, 3, {9, 1, 3}}, {VRRP_SNMP_SCRIPT_INTERVAL, ASN_INTEGER, RONLY, vrrp_snmp_script, 3, {9, 1, 4}}, {VRRP_SNMP_SCRIPT_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_script, 3, {9, 1, 5}}, {VRRP_SNMP_SCRIPT_RESULT, ASN_INTEGER, RONLY, vrrp_snmp_script, 3, {9, 1, 6}}, {VRRP_SNMP_SCRIPT_RISE, ASN_UNSIGNED, RONLY, vrrp_snmp_script, 3, {9, 1, 7}}, {VRRP_SNMP_SCRIPT_FALL, ASN_UNSIGNED, RONLY, vrrp_snmp_script, 3, {9, 1, 8}}, {VRRP_SNMP_SCRIPT_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_script, 3, {9, 1, 9}}, {VRRP_SNMP_SCRIPT_PATH, ASN_OCTET_STR, RONLY, vrrp_snmp_script, 3, {9, 1, 10}}, {VRRP_SNMP_SCRIPT_INTERVAL_USEC, ASN_UNSIGNED, RONLY, vrrp_snmp_script, 3, {9, 1, 11}}, {VRRP_SNMP_SCRIPT_TIMEOUT_USEC, ASN_UNSIGNED, RONLY, vrrp_snmp_script, 3, {9, 1, 12}}, /* vrrpRouteNextHopTable */ {VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS_TYPE, ASN_INTEGER, RONLY, vrrp_snmp_next_hop, 3, {11, 1, 2}}, {VRRP_SNMP_ROUTE_NEXT_HOP_ADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_next_hop, 3, {11, 1, 3}}, {VRRP_SNMP_ROUTE_NEXT_HOP_IF_INDEX, ASN_INTEGER, RONLY, vrrp_snmp_next_hop, 3, {11, 1, 4}}, {VRRP_SNMP_ROUTE_NEXT_HOP_IF_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_next_hop, 3, {11, 1, 5}}, {VRRP_SNMP_ROUTE_NEXT_HOP_WEIGHT, ASN_UNSIGNED, RONLY, vrrp_snmp_next_hop, 3, {11, 1, 6}}, {VRRP_SNMP_ROUTE_NEXT_HOP_ONLINK, ASN_INTEGER, RONLY, vrrp_snmp_next_hop, 3, {11, 1, 7}}, {VRRP_SNMP_ROUTE_NEXT_HOP_REALM_DST, ASN_UNSIGNED, RONLY, vrrp_snmp_next_hop, 3, {11, 1, 8}}, {VRRP_SNMP_ROUTE_NEXT_HOP_REALM_SRC, ASN_UNSIGNED, RONLY, vrrp_snmp_next_hop, 3, {11, 1, 9}}, #if HAVE_DECL_RTA_ENCAP {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TYPE, ASN_INTEGER, RONLY, vrrp_snmp_encap, 3, {11, 1, 10}}, #if HAVE_DECL_LWTUNNEL_ENCAP_MPLS {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_MPLS_LABELS, ASN_OCTET_STR, RONLY, vrrp_snmp_encap, 3, {11, 1, 11}}, #endif {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_ID, ASN_COUNTER64, RONLY, vrrp_snmp_encap, 3, {11, 1, 12}}, {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_DST_ADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_encap, 3, {11, 1, 13}}, {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_SRC_ADDRESS, ASN_OCTET_STR, RONLY, vrrp_snmp_encap, 3, {11, 1, 14}}, {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TOS, ASN_UNSIGNED, RONLY, vrrp_snmp_encap, 3, {11, 1, 15}}, {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_TTL, ASN_UNSIGNED, RONLY, vrrp_snmp_encap, 3, {11, 1, 16}}, {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_FLAGS, ASN_UNSIGNED, RONLY, vrrp_snmp_encap, 3, {11, 1, 17}}, #if HAVE_DECL_LWTUNNEL_ENCAP_ILA {VRRP_SNMP_ROUTE_NEXT_HOP_ENCAP_ILA_LOCATOR, ASN_COUNTER64, RONLY, vrrp_snmp_encap, 3, {11, 1, 18}}, #endif #endif /* vrrpTrackedFileTable */ {VRRP_SNMP_TRACKEDFILE_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_trackedfile, 3, {12, 1, 2}}, {VRRP_SNMP_TRACKEDFILE_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_trackedfile, 3, {12, 1, 3}}, {VRRP_SNMP_TRACKEDFILE_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_trackedfile, 3, {12, 1, 4}}, /* vrrpFileTable */ {VRRP_SNMP_FILE_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_file, 3, {13, 1, 2}}, {VRRP_SNMP_FILE_PATH, ASN_OCTET_STR, RONLY, vrrp_snmp_file, 3, {13, 1, 3}}, {VRRP_SNMP_FILE_RESULT, ASN_INTEGER, RONLY, vrrp_snmp_file, 3, {13, 1, 4}}, {VRRP_SNMP_FILE_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_file, 3, {13, 1, 5}}, {VRRP_SNMP_FILE_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_file, 3, {13, 1, 6}}, #ifdef _WITH_BFD_ /* vrrpTrackedBfdTable */ {VRRP_SNMP_TRACKEDBFD_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_trackedbfd, 3, {17, 1, 2}}, {VRRP_SNMP_TRACKEDBFD_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_trackedbfd, 3, {17, 1, 3}}, {VRRP_SNMP_TRACKEDBFD_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_trackedbfd, 3, {17, 1, 4}}, /* vrrpBfdTable */ {VRRP_SNMP_BFD_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_bfd, 3, {18, 1, 2}}, {VRRP_SNMP_BFD_RESULT, ASN_INTEGER, RONLY, vrrp_snmp_bfd, 3, {18, 1, 3}}, {VRRP_SNMP_BFD_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_bfd, 3, {18, 1, 4}}, {VRRP_SNMP_BFD_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_bfd, 3, {18, 1, 5}}, #endif #ifdef _WITH_TRACK_PROCESS_ /* vrrpTrackedProcessTable */ {VRRP_SNMP_TRACKEDPROCESS_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_trackedprocess, 3, {20, 1, 2}}, {VRRP_SNMP_TRACKEDPROCESS_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_trackedprocess, 3, {20, 1, 3}}, {VRRP_SNMP_TRACKEDPROCESS_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_trackedprocess, 3, {20, 1, 4}}, /* vrrpProcessTable */ {VRRP_SNMP_PROCESS_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_process, 3, {21, 1, 2}}, {VRRP_SNMP_PROCESS_PATH, ASN_OCTET_STR, RONLY, vrrp_snmp_process, 3, {21, 1, 3}}, {VRRP_SNMP_PROCESS_PARAMS, ASN_OCTET_STR, RONLY, vrrp_snmp_process, 3, {21, 1, 4}}, {VRRP_SNMP_PROCESS_PARAM_MATCH, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 5}}, {VRRP_SNMP_PROCESS_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 6}}, {VRRP_SNMP_PROCESS_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 7}}, {VRRP_SNMP_PROCESS_QUORUM, ASN_UNSIGNED, RONLY, vrrp_snmp_process, 3, {21, 1, 8}}, {VRRP_SNMP_PROCESS_QUORUM_MAX, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 9}}, {VRRP_SNMP_PROCESS_FORKDELAY, ASN_UNSIGNED, RONLY, vrrp_snmp_process, 3, {21, 1, 10}}, {VRRP_SNMP_PROCESS_TERMINATEDELAY, ASN_UNSIGNED, RONLY, vrrp_snmp_process, 3, {21, 1, 11}}, {VRRP_SNMP_PROCESS_FULLCOMMAND, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 12}}, {VRRP_SNMP_PROCESS_CURPROC, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 13}}, {VRRP_SNMP_PROCESS_RESULT, ASN_INTEGER, RONLY, vrrp_snmp_process, 3, {21, 1, 14}}, #endif /* syncGroupTrackedInterfaceTable */ {VRRP_SNMP_SGROUPTRACKEDINTERFACE_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_group_trackedinterface, 3, {14, 1, 1}}, {VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedinterface, 3, {14, 1, 2}}, {VRRP_SNMP_SGROUPTRACKEDINTERFACE_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedinterface, 3, {14, 1, 3}}, /* syncGroupTrackedScriptTable */ {VRRP_SNMP_SGROUPTRACKEDSCRIPT_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_group_trackedscript, 3, {15, 1, 2}}, {VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedscript, 3, {15, 1, 3}}, {VRRP_SNMP_SGROUPTRACKEDSCRIPT_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedscript, 3, {15, 1, 4}}, /* syncGroupTrackedFileTable */ {VRRP_SNMP_SGROUPTRACKEDFILE_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_group_trackedfile, 3, {16, 1, 2}}, {VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedfile, 3, {16, 1, 3}}, {VRRP_SNMP_SGROUPTRACKEDFILE_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedfile, 3, {16, 1, 4}}, #ifdef _WITH_BFD_ /* syncGroupTrackedBfdTable */ {VRRP_SNMP_SGROUPTRACKEDBFD_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_group_trackedbfd, 3, {19, 1, 2}}, {VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedbfd, 3, {19, 1, 3}}, {VRRP_SNMP_SGROUPTRACKEDBFD_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedbfd, 3, {19, 1, 4}}, #endif #ifdef _WITH_TRACK_PROCESS_ /* syncGroupTrackedProcessTable */ {VRRP_SNMP_SGROUPTRACKEDPROCESS_NAME, ASN_OCTET_STR, RONLY, vrrp_snmp_group_trackedprocess, 3, {22, 1, 2}}, {VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedprocess, 3, {22, 1, 3}}, {VRRP_SNMP_SGROUPTRACKEDPROCESS_WEIGHT_REVERSE, ASN_INTEGER, RONLY, vrrp_snmp_group_trackedprocess, 3, {22, 1, 4}}, #endif }; void vrrp_snmp_instance_trap(vrrp_t *vrrp) { /* OID of the notification */ oid notification_oid[] = { VRRP_OID, 10, 0, 2 }; size_t notification_oid_len = OID_LENGTH(notification_oid); /* OID for snmpTrapOID.0 */ oid objid_snmptrap[] = { SNMPTRAP_OID }; size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); snmp_ret_t ptr_conv; /* Other OID */ oid name_oid[] = { VRRP_OID, 3, 1, 2 }; size_t name_oid_len = OID_LENGTH(name_oid); oid state_oid[] = { VRRP_OID, 3, 1, 4 }; size_t state_oid_len = OID_LENGTH(state_oid); oid initialstate_oid[] = { VRRP_OID, 3, 1, 5}; size_t initialstate_oid_len = OID_LENGTH(initialstate_oid); oid routerId_oid[] = { KEEPALIVED_OID, 1, 2, 0 }; size_t routerId_oid_len = OID_LENGTH(routerId_oid); netsnmp_variable_list *notification_vars = NULL; int state = 4; /* unknown */ if (!global_data->enable_traps || !global_data->enable_snmp_vrrp) return; if (vrrp->state == VRRP_STATE_INIT || vrrp->state == VRRP_STATE_BACK || vrrp->state == VRRP_STATE_MAST || vrrp->state == VRRP_STATE_FAULT) state = vrrp->state; else if (vrrp->state == VRRP_STATE_STOP) state = 5; else if (vrrp->state == VRRP_STATE_DELETED) state = 6; /* snmpTrapOID */ snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, objid_snmptrap_len, ASN_OBJECT_ID, PTR_CAST(u_char, notification_oid), notification_oid_len * sizeof(oid)); /* vrrpInstanceName */ ptr_conv.cp = vrrp->iname; snmp_varlist_add_variable(¬ification_vars, name_oid, name_oid_len, ASN_OCTET_STR, ptr_conv.p, strlen(vrrp->iname)); /* vrrpInstanceState */ snmp_varlist_add_variable(¬ification_vars, state_oid, state_oid_len, ASN_INTEGER, PTR_CAST(u_char, &state), sizeof(state)); /* vrrpInstanceInitialState */ snmp_varlist_add_variable(¬ification_vars, initialstate_oid, initialstate_oid_len, ASN_INTEGER, PTR_CAST(u_char, &vrrp->configured_state), sizeof(vrrp->configured_state)); /* routerId */ ptr_conv.cp = global_data->router_id; snmp_varlist_add_variable(¬ification_vars, routerId_oid, routerId_oid_len, ASN_OCTET_STR, ptr_conv.p, strlen(global_data->router_id)); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Sending SNMP notification", vrrp->iname); send_v2trap(notification_vars); snmp_free_varbind(notification_vars); } void vrrp_snmp_group_trap(vrrp_sgroup_t *group) { /* OID of the notification */ oid notification_oid[] = { VRRP_OID, 10, 0, 1 }; size_t notification_oid_len = OID_LENGTH(notification_oid); /* OID for snmpTrapOID.0 */ oid objid_snmptrap[] = { SNMPTRAP_OID }; size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); snmp_ret_t ptr_conv; /* Other OID */ oid name_oid[] = { VRRP_OID, 1, 1, 2 }; size_t name_oid_len = OID_LENGTH(name_oid); oid state_oid[] = { VRRP_OID, 1, 1, 3 }; size_t state_oid_len = OID_LENGTH(state_oid); oid routerId_oid[] = { KEEPALIVED_OID, 1, 2, 0 }; size_t routerId_oid_len = OID_LENGTH(routerId_oid); netsnmp_variable_list *notification_vars = NULL; int state = 4; /* unknown */ if (!global_data->enable_traps || !global_data->enable_snmp_vrrp) return; if (group->state == VRRP_STATE_INIT || group->state == VRRP_STATE_BACK || group->state == VRRP_STATE_MAST || group->state == VRRP_STATE_FAULT) state = group->state; else if (group->state == VRRP_STATE_STOP) state = 5; /* snmpTrapOID */ snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, objid_snmptrap_len, ASN_OBJECT_ID, PTR_CAST(u_char, notification_oid), notification_oid_len * sizeof(oid)); /* vrrpSyncGroupName */ ptr_conv.cp = group->gname; snmp_varlist_add_variable(¬ification_vars, name_oid, name_oid_len, ASN_OCTET_STR, ptr_conv.p, strlen(group->gname)); /* vrrpSyncGroupState */ snmp_varlist_add_variable(¬ification_vars, state_oid, state_oid_len, ASN_INTEGER, PTR_CAST(u_char, &state), sizeof(state)); /* routerId */ ptr_conv.cp = global_data->router_id; snmp_varlist_add_variable(¬ification_vars, routerId_oid, routerId_oid_len, ASN_OCTET_STR, ptr_conv.p, strlen(global_data->router_id)); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "VRRP_Group(%s): Sending SNMP notification", group->gname); send_v2trap(notification_vars); snmp_free_varbind(notification_vars); } #endif #if defined _WITH_SNMP_RFC_ /* Convert VRRP state to RFC SNMP state */ static int vrrp_snmp_rfc_state(int state) { if (state <= VRRP_STATE_MAST) return state + 1; return VRRP_STATE_INIT + 1; } #endif #ifdef _WITH_SNMP_RFCV2_ static bool suitable_for_rfc2787(const vrrp_t* vrrp) { #ifdef _WITH_SNMP_RFCV3_ /* We mustn't return any VRRP instances that aren't version 2 */ if (vrrp->version != VRRP_VERSION_2) return false; #endif /* We have to skip VRRPv2 with IPv6 since it won't be understood */ if (vrrp->family == AF_INET6) return false; /* We are expected to have at least one VIP */ if (list_empty(&vrrp->vip)) return false; return true; } static ip_address_t* vrrp_rfcv2_header_ar_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { ip_address_t *vip; vrrp_t *vrrp; ip_address_t *bel = NULL; oid * target, current[2], best[2]; struct in_addr best_addr, target_addr, current_addr; int result, result2 = 0; size_t target_len; bool found_exact = false; bool found_better; *write_method = 0; *var_len = sizeof(unsigned long); if (list_empty(&vrrp_data->vrrp)) return NULL; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } /* We search the best match: equal if exact, the lower OID in the set of the OID strictly superior to the target otherwise. */ best[0] = best[1] = MAX_SUBID; /* Our best match */ best_addr.s_addr = 0xffffffff; target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; if (target_len > 2 ) { target_len = 2; target_addr.s_addr = (in_addr_t)(name[*length - 4] << 24 | name[*length - 3] << 16 | name[*length - 2] << 8 | name[*length - 1]); } else target_addr.s_addr = 0; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!suitable_for_rfc2787(vrrp)) continue; current[0] = vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0; current[1] = vrrp->vrid; if ((result = snmp_oid_compare(current, 2, target, target_len)) < 0) continue; if (exact) { if (result > 0) continue; } else { if ((result2 = snmp_oid_compare(current, 2, best, 2)) > 0) continue; } if (list_empty(&vrrp->vip)) { if (exact) return NULL; continue; } found_better = false; list_for_each_entry(vip, &vrrp->vip, e_list) { /* We need the address to be MSB first, for numerical comparison */ current_addr.s_addr = htonl(vip->u.sin.sin_addr.s_addr); if (exact) { if (target_addr.s_addr == current_addr.s_addr) { memcpy(best, current, sizeof(best)); best_addr = current_addr; bel = vip; found_exact = true; break; } continue; } if (result == 0 && target_len && current_addr.s_addr <= target_addr.s_addr) continue; if (result2 == 0 && current_addr.s_addr >= best_addr.s_addr) continue; memcpy(best, current, sizeof(best)); best_addr = current_addr; bel = vip; result2 = 0; found_better = true; } if (found_exact) break; if (exact) return NULL; if (result == 0 && found_better) break; } if (bel == NULL) /* No best match */ return NULL; /* Let's use our best match */ memcpy(target, best, sizeof(best)); *length = (unsigned)vp->namelen + 2; name[*length] = best_addr.s_addr >> 24; name[*length+1] = (best_addr.s_addr >> 16) & 0xff; name[*length+2] = (best_addr.s_addr >> 8) & 0xff; name[*length+3] = (best_addr.s_addr ) & 0xff; *length += 4; return bel; } static u_char* vrrp_rfcv2_snmp_node_info(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; if (vp->magic == VRRP_RFC_SNMP_NODE_VER) { long_ret.u = 2; return PTR_CAST(u_char, &long_ret); } if (vp->magic == VRRP_RFC_SNMP_NOTIF_CNTL) { long_ret.u = global_data->enable_traps ? 1 : 2 ; return PTR_CAST(u_char, &long_ret); } return NULL; } static vrrp_t* snmp_rfcv2_header_list_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_t *bel = NULL, *vrrp; oid * target, current[2], best[2]; int result; size_t target_len; *write_method = 0; *var_len = sizeof (unsigned long); if (list_empty(&vrrp_data->vrrp)) return NULL; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } /* We search the best match: equal if exact, the lower OID in the set of the OID strictly superior to the target otherwise. */ best[0] = best[1] = MAX_SUBID; /* Our best match */ target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!suitable_for_rfc2787(vrrp)) continue; if (target_len && (vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0 < target[0])) continue; /* Optimization: cannot be part of our set */ current[0] = vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0; current[1] = vrrp->vrid; if ((result = snmp_oid_compare(current, 2, target, target_len)) < 0) continue; if (result == 0) { if (!exact) continue; return vrrp; } if (snmp_oid_compare(current, 2, best, 2) < 0) { /* This is our best match */ memcpy(best, current, sizeof(best)); bel = vrrp; } } if (bel == NULL) /* No best match */ return NULL; if (exact) /* No exact match */ return NULL; /* Let's use our best match */ memcpy(target, best, sizeof(best)); *length = (unsigned)vp->namelen + 2; return bel; } static u_char* vrrp_rfcv2_snmp_opertable(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_t *rt; interface_t* ifp; timeval_t uptime; if ((rt = snmp_rfcv2_header_list_table(vp, name, length, exact, var_len, write_method)) == NULL) return NULL; switch (vp->magic) { case VRRP_RFC_SNMP_OPER_VMAC: if (!rt->ifp) return NULL; *var_len = rt->ifp->hw_addr_len; return PTR_CAST(u_char, &rt->ifp->hw_addr); case VRRP_RFC_SNMP_OPER_STATE: long_ret.s = vrrp_snmp_rfc_state(rt->state); return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_ADM_STATE: /* If we implement write access, then this could be 2 for down */ long_ret.u = 1; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_PRI: long_ret.u = rt->base_priority; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_ADDR_CNT: long_ret.u = rt->vip_cnt; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_MIP: *var_len = sizeof PTR_CAST(struct sockaddr_in, &rt->master_saddr)->sin_addr.s_addr; return PTR_CAST2(u_char, struct sockaddr_in, &rt->master_saddr, sin_addr.s_addr); case VRRP_RFC_SNMP_OPER_PIP: if (!rt->ifp) return NULL; #ifdef _HAVE_VRRP_VMAC_ if (IS_MAC_IP_VLAN(rt->ifp)) ifp = rt->ifp->base_ifp; else #endif ifp = rt->ifp; *var_len = sizeof ifp->sin_addr; return PTR_CAST(u_char, &ifp->sin_addr); case VRRP_RFC_SNMP_OPER_AUTH_TYPE: #ifdef _WITH_VRRP_AUTH_ long_ret.s = rt->auth_type + 1; #else long_ret.s = 1; #endif return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_AUTH_KEY: *var_len = 0; // Not readable return NULL; case VRRP_RFC_SNMP_OPER_ADVERT_INT: long_ret.u = rt->adver_int / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_PREEMPT: long_ret.u = SNMP_TruthValue(!__test_bit(VRRP_FLAG_NOPREEMPT, &rt->flags)); return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_VR_UPTIME: if (rt->state == VRRP_STATE_BACK || rt->state == VRRP_STATE_MAST) { timersub(&rt->stats->uptime, &snmp_vrrp_start_time, &uptime); long_ret.s = uptime.tv_sec * 100 + uptime.tv_usec / 10000; // unit is centi-seconds } else long_ret.u = 0; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_PROTO: long_ret.u = 1; // IP return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_OPER_ROW_STAT: long_ret.u = 1; // active - 1, notInService - 2, notReady - 3, createAndGo - 4, createAndWait - 5 return PTR_CAST(u_char, &long_ret); } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_rfcv2_snmp_opertable(vp, name, length, exact, var_len, write_method); return NULL; } static u_char* vrrp_rfcv2_snmp_assoiptable(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { ip_address_t *addr; if (snmp_oid_compare(name, *length, vp->name, vp->namelen) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; *var_len = 0; return NULL; } if ((addr = vrrp_rfcv2_header_ar_table(vp, name, length, exact, var_len, write_method)) == NULL) return NULL; switch (vp->magic) { case VRRP_RFC_SNMP_ASSOC_IP_ADDR_ROW: /* If we implement write access, then this could be 2 for down */ long_ret.u = 1; return PTR_CAST(u_char, &long_ret); } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_rfcv2_snmp_assoiptable(vp, name, length, exact, var_len, write_method); return NULL; } static u_char* vrrp_rfcv2_snmp_stats(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_t *vrrp; if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; if (vp->magic != VRRP_RFC_SNMP_STATS_CHK_ERR && vp->magic != VRRP_RFC_SNMP_STATS_VER_ERR && vp->magic != VRRP_RFC_SNMP_STATS_VRID_ERR) return NULL; long_ret.u = 0; if (list_empty(&vrrp_data->vrrp)) return PTR_CAST(u_char, &long_ret); /* Work through all the vrrp instances that we can respond for */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!suitable_for_rfc2787(vrrp)) continue; switch (vp->magic) { case VRRP_RFC_SNMP_STATS_CHK_ERR: long_ret.u += vrrp->stats->chk_err; break; case VRRP_RFC_SNMP_STATS_VER_ERR: long_ret.u += vrrp->stats->vers_err; break; case VRRP_RFC_SNMP_STATS_VRID_ERR: long_ret.u += vrrp->stats->vrid_err; break; } } return PTR_CAST(u_char, &long_ret); } static u_char* vrrp_rfcv2_snmp_statstable(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_t *rt; if ((rt = snmp_rfcv2_header_list_table(vp, name, length, exact, var_len, write_method)) == NULL) return NULL; switch (vp->magic) { case VRRP_RFC_SNMP_STATS_MASTER: long_ret.u = rt->stats->become_master; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_ADV_RCVD: long_ret.u = rt->stats->advert_rcvd; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_ADV_INT_ERR: long_ret.u = rt->stats->advert_interval_err; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_AUTH_FAIL: #ifdef _WITH_VRRP_AUTH_ long_ret.u = rt->stats->auth_failure; #else long_ret.u = 0; #endif return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_TTL_ERR: long_ret.u = rt->stats->ip_ttl_err; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_PRI_0_RCVD: long_ret.u = rt->stats->pri_zero_rcvd; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_PRI_0_SENT: long_ret.u = rt->stats->pri_zero_sent; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_INV_TYPE_RCVD: long_ret.u = rt->stats->invalid_type_rcvd; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_ADDR_LIST_ERR: long_ret.u = rt->stats->addr_list_err; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_AUTH_INV: long_ret.u = rt->stats->invalid_authtype; return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_AUTH_MIS: #ifdef _WITH_VRRP_AUTH_ long_ret.u = rt->stats->authtype_mismatch; #else long_ret.u = 0; #endif return PTR_CAST(u_char, &long_ret); case VRRP_RFC_SNMP_STATS_PL_ERR: long_ret.u = rt->stats->packet_len_err; return PTR_CAST(u_char, &long_ret); } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_rfcv2_snmp_statstable(vp, name, length, exact, var_len, write_method); return NULL; } static oid vrrp_rfcv2_oid[] = {VRRP_RFC_OID}; static struct variable4 vrrp_rfcv2_vars[] = { { VRRP_RFC_SNMP_NODE_VER, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_node_info, 2, {1, 1}}, { VRRP_RFC_SNMP_NOTIF_CNTL, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_node_info, 2, {1, 2}}, /* vrrpOperTable */ { VRRP_RFC_SNMP_OPER_VMAC, ASN_OCTET_STR, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 2}}, { VRRP_RFC_SNMP_OPER_STATE, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 3}}, { VRRP_RFC_SNMP_OPER_ADM_STATE, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 4}}, { VRRP_RFC_SNMP_OPER_PRI, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 5}}, { VRRP_RFC_SNMP_OPER_ADDR_CNT, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 6}}, { VRRP_RFC_SNMP_OPER_MIP, ASN_IPADDRESS, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 7}}, { VRRP_RFC_SNMP_OPER_PIP, ASN_IPADDRESS, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 8}}, { VRRP_RFC_SNMP_OPER_AUTH_TYPE, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 9}}, { VRRP_RFC_SNMP_OPER_AUTH_KEY, ASN_OCTET_STR, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 10}}, { VRRP_RFC_SNMP_OPER_ADVERT_INT, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 11}}, { VRRP_RFC_SNMP_OPER_PREEMPT, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 12}}, { VRRP_RFC_SNMP_OPER_VR_UPTIME, ASN_TIMETICKS, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 13}}, { VRRP_RFC_SNMP_OPER_PROTO, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 14}}, { VRRP_RFC_SNMP_OPER_ROW_STAT, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_opertable, 4, {1, 3, 1, 15}}, /* vrrpAssoIpAddrTable */ { VRRP_RFC_SNMP_ASSOC_IP_ADDR_ROW, ASN_INTEGER, RONLY, vrrp_rfcv2_snmp_assoiptable, 4, {1, 4, 1, 2}}, /* vrrpRouterStats */ { VRRP_RFC_SNMP_STATS_CHK_ERR, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_stats, 2, {2, 1}}, { VRRP_RFC_SNMP_STATS_VER_ERR, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_stats, 2, {2, 2}}, { VRRP_RFC_SNMP_STATS_VRID_ERR, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_stats, 2, {2, 3}}, /* vrrpRouterStatsTable */ { VRRP_RFC_SNMP_STATS_MASTER, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 1}}, { VRRP_RFC_SNMP_STATS_ADV_RCVD, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 2}}, { VRRP_RFC_SNMP_STATS_ADV_INT_ERR, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 3}}, { VRRP_RFC_SNMP_STATS_AUTH_FAIL, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 4}}, { VRRP_RFC_SNMP_STATS_TTL_ERR, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 5}}, { VRRP_RFC_SNMP_STATS_PRI_0_RCVD, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 6}}, { VRRP_RFC_SNMP_STATS_PRI_0_SENT, ASN_COUNTER , RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 7}}, { VRRP_RFC_SNMP_STATS_INV_TYPE_RCVD, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 8}}, { VRRP_RFC_SNMP_STATS_ADDR_LIST_ERR, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 9}}, { VRRP_RFC_SNMP_STATS_AUTH_INV, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 10}}, { VRRP_RFC_SNMP_STATS_AUTH_MIS, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 11}}, { VRRP_RFC_SNMP_STATS_PL_ERR, ASN_COUNTER, RONLY, vrrp_rfcv2_snmp_statstable, 4, {2, 4, 1, 12}} }; void vrrp_rfcv2_snmp_new_master_trap(vrrp_t *vrrp) { /* OID of the notification vrrpTrapNewMaster */ oid notification_oid[] = { VRRP_RFC_TRAP_OID, 1 }; size_t notification_oid_len = OID_LENGTH(notification_oid); /* OID for snmpTrapOID.0 */ oid objid_snmptrap[] = { SNMPTRAP_OID }; size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); /* OID for trap data vrrpOperMasterIPAddr */ oid masterip_oid[] = { VRRP_RFC_OID, 1, 3, 1, 7, vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0, vrrp->vrid }; size_t masterip_oid_len = OID_LENGTH(masterip_oid); netsnmp_variable_list *notification_vars = NULL; if (!global_data->enable_traps || !global_data->enable_snmp_rfcv2) return; if (!suitable_for_rfc2787(vrrp)) return; /* snmpTrapOID */ snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, objid_snmptrap_len, ASN_OBJECT_ID, PTR_CAST(u_char, notification_oid), notification_oid_len * sizeof(oid)); /* vrrpInstanceName */ snmp_varlist_add_variable(¬ification_vars, masterip_oid, masterip_oid_len, ASN_IPADDRESS, PTR_CAST2(u_char, struct sockaddr_in, &vrrp->saddr, sin_addr.s_addr), sizeof PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr.s_addr); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Sending SNMP notification" " vrrpTrapNewMaster" , vrrp->iname); send_v2trap(notification_vars); snmp_free_varbind(notification_vars); } void vrrp_rfcv2_snmp_auth_err_trap(vrrp_t *vrrp, struct in_addr src, enum rfcv2_trap_auth_error_type auth_err) { /* OID of the notification vrrpTrapNewMaster */ oid notification_oid[] = { VRRP_RFC_TRAP_OID, 2 }; size_t notification_oid_len = OID_LENGTH(notification_oid); /* OID for snmpTrapOID.0 */ oid objid_snmptrap[] = { SNMPTRAP_OID }; size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); /* OID for trap data vrrpTrapPacketSrc */ oid packet_src_oid[] = { VRRP_RFC_OID, 1, 5, vrrp->ifp ? IF_INDEX(vrrp->ifp) : 0, vrrp->vrid }; size_t packet_src_oid_len = OID_LENGTH(packet_src_oid); /* OID for trap data vrrpTrapAuthErrorType */ oid err_type_oid[] = { VRRP_RFC_OID, 1, 6, vrrp->ifp ? IF_INDEX(vrrp->ifp) : 0, vrrp->vrid }; size_t err_type_oid_len = OID_LENGTH(err_type_oid); netsnmp_variable_list *notification_vars = NULL; if (!global_data->enable_traps || !global_data->enable_snmp_rfcv2) return; if (!suitable_for_rfc2787(vrrp)) return; /* snmpTrapOID */ snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, objid_snmptrap_len, ASN_OBJECT_ID, PTR_CAST(u_char, notification_oid), notification_oid_len * sizeof(oid)); /* vrrpPacketSrc */ snmp_varlist_add_variable(¬ification_vars, packet_src_oid, packet_src_oid_len, ASN_IPADDRESS, PTR_CAST(u_char, &src), sizeof(src)); /* vrrpAuthErrorType */ snmp_varlist_add_variable(¬ification_vars, err_type_oid, err_type_oid_len, ASN_INTEGER, PTR_CAST(u_char, &auth_err), sizeof(auth_err)); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Sending SNMP notification" " vrrpTrapAuthFailure" , vrrp->iname); send_v2trap(notification_vars); snmp_free_varbind(notification_vars); } #endif #ifdef _WITH_SNMP_RFCV3_ static bool suitable_for_rfc6527(const vrrp_t* vrrp) { #ifndef _SNMP_REPLY_V3_FOR_V2_ /* We mustn't return any VRRP instances that don't match version */ if (vrrp->version != VRRP_VERSION_3) return false; #endif /* We are expected to have at least one VIP */ if (list_empty(&vrrp->vip)) return false; return true; } static inline int inet6_addr_compare(const struct in6_addr* l, const struct in6_addr* r) { size_t i; uint32_t l1, r1; for (i = 0; i < sizeof(l->s6_addr32) / sizeof(l->s6_addr32[0]); i++) { l1 = htonl(l->s6_addr32[i]); r1 = htonl(r->s6_addr32[i]); if (l1 != r1) return ((l1 > r1) & 1) * 2 - 1; } return 0; } static ip_address_t* vrrp_rfcv3_header_ar_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { ip_address_t *vip; vrrp_t *vrrp; ip_address_t *bel = NULL; oid * target, current[3], best[3]; struct in_addr target_addr, current_addr, best_addr; struct in6_addr target_addr6, current_addr6, best_addr6; int result, result2 = 0; size_t target_len; bool found_exact = false; bool found_better; size_t i; *write_method = 0; *var_len = sizeof(unsigned long); if (list_empty(&vrrp_data->vrrp)) return NULL; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } /* We search the best match: equal if exact, the lower OID in the set of the OID strictly superior to the target otherwise. */ best[0] = best[1] = best[2] = MAX_SUBID; /* Our best match */ best_addr.s_addr = 0xffffffff; memset(&best_addr6, 0xff, sizeof(best_addr6)); target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; target_addr.s_addr = 0; /* Avoid compiler uninitialised warning */ if (target_len == 3 + sizeof(struct in_addr)) { target_len = 3; target_addr.s_addr = (in_addr_t)(name[*length - 4] << 24 | name[*length - 3] << 16 | name[*length - 2] << 8 | name[*length - 1]); } else if (target_len == 3 + sizeof(struct in6_addr)) { target_len = 3; for (i = 0; i < sizeof (struct in6_addr); i++) target_addr6.s6_addr[i] = (uint8_t)name[*length - sizeof(struct in6_addr) + i]; } list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!suitable_for_rfc6527(vrrp)) continue; current[0] = vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0; current[1] = vrrp->vrid; current[2] = SNMP_InetAddressType(vrrp->family); if ((result = snmp_oid_compare(current, 3, target, target_len)) < 0) continue; if (exact) { if (result != 0) continue; } else { if ((result2 = snmp_oid_compare(current, 3, best, 3)) > 0) continue; } if (list_empty(&vrrp->vip)) { if (exact) return NULL; continue; } found_better = false; list_for_each_entry(vip, &vrrp->vip, e_list) { if (vrrp->family == AF_INET) { current_addr.s_addr = htonl(vip->u.sin.sin_addr.s_addr); if (exact) { if (target_addr.s_addr == current_addr.s_addr) { memcpy(best, current, sizeof(best)); best_addr = current_addr; bel = vip; found_exact = true; break; } continue; } if (result == 0 && target_len && current_addr.s_addr <= target_addr.s_addr) continue; if (result2 == 0 && current_addr.s_addr >= best_addr.s_addr) continue; memcpy(best, current, sizeof(best)); best_addr = current_addr; bel = vip; result2 = 0; found_better = true; } else { current_addr6 = vip->u.sin6_addr; if (exact) { if (inet6_addr_compare(&target_addr6, ¤t_addr6) == 0) { memcpy(best, current, sizeof(best)); best_addr6 = current_addr6; bel = vip; found_exact = true; break; } continue; } if (result == 0 && target_len && inet6_addr_compare(¤t_addr6, &target_addr6) <= 0) continue; if (result2 == 0 && inet6_addr_compare(¤t_addr6, &best_addr6) >= 0) continue; memcpy(best, current, sizeof(best)); best_addr6 = current_addr6; bel = vip; result2 = 0; found_better = true; } } if (found_exact) break; if (exact) return NULL; if (result == 0 && found_better) break; } if (bel == NULL) /* No best match */ return NULL; /* Let's use our best match */ memcpy(target, best, sizeof(best)); *length = (unsigned)vp->namelen + 3; if (name[*length - 1] == 1) { name[*length ] = best_addr.s_addr >> 24; name[*length+1] = (best_addr.s_addr >> 16) & 0xff; name[*length+2] = (best_addr.s_addr >> 8) & 0xff; name[*length+3] = (best_addr.s_addr ) & 0xff; *length += sizeof(struct in_addr); } else { for (i = 0; i < sizeof(struct in6_addr); i++) name[*length + i] = best_addr6.s6_addr[i]; *length += sizeof(struct in6_addr); } return bel; } static vrrp_t* snmp_rfcv3_header_list_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_t *bel = NULL, *vrrp; oid * target, current[3], best[3]; int result; size_t target_len; *write_method = 0; *var_len = sizeof (unsigned long); if (list_empty(&vrrp_data->vrrp)) return NULL; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } /* We search the best match: equal if exact, the lower OID in the set of the OID strictly superior to the target otherwise. */ best[0] = best[1] = best[2] = MAX_SUBID; /* Our best match */ target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!suitable_for_rfc6527(vrrp)) continue; if (target_len && ((vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0) < target[0] || ((vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0) == target[0] && vrrp->vrid < target[1]))) continue; /* Optimization: cannot be part of our set */ current[0] = vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0; current[1] = vrrp->vrid; current[2] = SNMP_InetAddressType(vrrp->family); if ((result = snmp_oid_compare(current, 3, target, target_len)) < 0) continue; if (result == 0) { if (!exact) continue; return vrrp; } if (snmp_oid_compare(current, 3, best, 3) < 0) { /* This is our best match */ memcpy(best, current, sizeof(best)); bel = vrrp; } } if (bel == NULL) /* No best match */ return NULL; if (exact) /* No exact match */ return NULL; /* Let's use our best match */ memcpy(target, best, sizeof(best)); *length = (unsigned)vp->namelen + 3; return bel; } static u_char* vrrp_rfcv3_snmp_opertable(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { vrrp_t *rt; interface_t* ifp; timeval_t uptime, cur_time; if ((rt = snmp_rfcv3_header_list_table(vp, name, length, exact, var_len, write_method)) == NULL) return NULL; switch (vp->magic) { case VRRP_RFCv3_SNMP_OPER_MIP: if (rt->state != VRRP_STATE_MAST) { if (rt->family == AF_INET) { *var_len = sizeof(struct in_addr); return PTR_CAST2(u_char, struct sockaddr_in, &rt->master_saddr, sin_addr); } *var_len = sizeof(struct in6_addr); return PTR_CAST2(u_char, struct sockaddr_in6, &rt->master_saddr, sin6_addr); } /* If we are master, we want to return the Primary IP address */ /* Falls through. */ case VRRP_RFCv3_SNMP_OPER_PIP: if (!rt->ifp) return NULL; #ifdef _HAVE_VRRP_VMAC_ if (IS_MAC_IP_VLAN(rt->ifp)) ifp = rt->ifp->base_ifp; else #endif ifp = rt->ifp; if (rt->family == AF_INET) { *var_len = sizeof(struct in_addr); return PTR_CAST(u_char, &ifp->sin_addr); } *var_len = sizeof(struct in6_addr); return PTR_CAST(u_char, &ifp->sin6_addr); case VRRP_RFCv3_SNMP_OPER_VMAC: if (!rt->ifp) return NULL; *var_len = rt->ifp->hw_addr_len; return PTR_CAST(u_char, &rt->ifp->hw_addr); case VRRP_RFCv3_SNMP_OPER_STATE: long_ret.s = vrrp_snmp_rfc_state(rt->state); return PTR_CAST(u_char, &long_ret); case VRRP_RFCv3_SNMP_OPER_PRI: long_ret.u = rt->base_priority; return PTR_CAST(u_char, &long_ret); case VRRP_RFCv3_SNMP_OPER_ADDR_CNT: long_ret.u = rt->vip_cnt; return PTR_CAST(u_char, &long_ret); case VRRP_RFCv3_SNMP_OPER_ADVERT_INT: long_ret.u = rt->adver_int / TIMER_CENTI_HZ; return PTR_CAST(u_char, &long_ret); case VRRP_RFCv3_SNMP_OPER_PREEMPT: long_ret.u = SNMP_TruthValue(!__test_bit(VRRP_FLAG_NOPREEMPT, &rt->flags)); return PTR_CAST(u_char, &long_ret); #ifdef _WITH_FIREWALL_ case VRRP_RFCv3_SNMP_OPER_ACCEPT: long_ret.u = SNMP_TruthValue(rt->accept); return PTR_CAST(u_char, &long_ret); #endif case VRRP_RFCv3_SNMP_OPER_VR_UPTIME: if (rt->state == VRRP_STATE_BACK || rt->state == VRRP_STATE_MAST) { cur_time = timer_now(); timersub(&cur_time, &rt->stats->uptime, &uptime); long_ret.s = uptime.tv_sec * 100 + uptime.tv_usec / 10000; // unit is centi-seconds } else long_ret.s = 0; return PTR_CAST(u_char, &long_ret); case VRRP_RFCv3_SNMP_OPER_ROW_STATUS: long_ret.u = 1; // active - 1, notInService - 2, notReady - 3, createAndGo - 4, createAndWait - 5 return PTR_CAST(u_char, &long_ret); } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_rfcv3_snmp_opertable(vp, name, length, exact, var_len, write_method); return NULL; } static u_char* vrrp_rfcv3_snmp_assoiptable(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { ip_address_t *addr; if (snmp_oid_compare(name, *length, vp->name, vp->namelen) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; *var_len = 0; } if ((addr = vrrp_rfcv3_header_ar_table(vp, name, length, exact, var_len, write_method)) == NULL) return NULL; switch (vp->magic) { case VRRP_RFCv3_SNMP_ASSOC_IP_ADDR_ROW_STATUS: /* If we implement write access, then this could be 2 for down */ long_ret.u = 1; return PTR_CAST(u_char, &long_ret); } /* If we are here, we asked for a non existent data. Try the next one. NOTE: This never appears to be true, so could be removed. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_rfcv3_snmp_assoiptable(vp, name, length, exact, var_len, write_method); return NULL; } static u_char* vrrp_rfcv3_snmp_stats(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { static struct counter64 c64; static uint32_t ret; vrrp_t *vrrp; uint64_t count; if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; if (vp->magic != VRRP_RFCv3_SNMP_STATS_CHK_ERR && vp->magic != VRRP_RFCv3_SNMP_STATS_VER_ERR && vp->magic != VRRP_RFCv3_SNMP_STATS_VRID_ERR && vp->magic != VRRP_RFCv3_SNMP_STATS_DISC_TIME) return NULL; c64.high = c64.low = 0; *var_len = sizeof(c64); if (list_empty(&vrrp_data->vrrp)) return PTR_CAST(u_char, &c64); count = 0; /* We don't do discontinuity time at the moment */ if (vp->magic == VRRP_RFCv3_SNMP_STATS_DISC_TIME) { // We don't "do" discontinuities *var_len = sizeof(ret); ret = 0; return PTR_CAST(u_char, &ret); } /* Work through all the vrrp instances that we can respond for */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!suitable_for_rfc6527(vrrp)) continue; switch (vp->magic) { case VRRP_RFCv3_SNMP_STATS_CHK_ERR: count += vrrp->stats->chk_err; break; case VRRP_RFCv3_SNMP_STATS_VER_ERR: count += vrrp->stats->vers_err; break; case VRRP_RFCv3_SNMP_STATS_VRID_ERR: count += vrrp->stats->vrid_err; break; } } set_counter64(&c64, count); return PTR_CAST(u_char, &c64); } static u_char* vrrp_rfcv3_snmp_statstable(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { static uint32_t ret; static struct counter64 c64; vrrp_t *rt; if ((rt = snmp_rfcv3_header_list_table(vp, name, length, exact, var_len, write_method)) == NULL) return NULL; *var_len = sizeof(c64); switch (vp->magic) { case VRRP_RFCv3_SNMP_STATS_MASTER: *var_len = sizeof(ret); ret = rt->stats->become_master; return PTR_CAST(u_char, &ret); case VRRP_RFCv3_SNMP_STATS_MASTER_REASON: ret = rt->stats->master_reason; *var_len = sizeof(ret); return PTR_CAST(u_char, &ret); case VRRP_RFCv3_SNMP_STATS_ADV_RCVD: set_counter64(&c64, rt->stats->advert_rcvd); return PTR_CAST(u_char, &c64); case VRRP_RFCv3_SNMP_STATS_ADV_INT_ERR: set_counter64(&c64, rt->stats->advert_interval_err); return PTR_CAST(u_char, &c64); case VRRP_RFCv3_SNMP_STATS_TTL_ERR: set_counter64(&c64, rt->stats->ip_ttl_err); return PTR_CAST(u_char, &c64); case VRRP_RFCv3_SNMP_STATS_PROTO_ERR_REASON: *var_len = sizeof(ret); ret = rt->stats->proto_err_reason; return PTR_CAST(u_char, &ret); case VRRP_RFCv3_SNMP_STATS_PRI_0_RCVD: set_counter64(&c64, rt->stats->pri_zero_rcvd); return PTR_CAST(u_char, &c64); case VRRP_RFCv3_SNMP_STATS_PRI_0_SENT: set_counter64(&c64, rt->stats->pri_zero_sent); return PTR_CAST(u_char, &c64); case VRRP_RFCv3_SNMP_STATS_INV_TYPE_RCVD: set_counter64(&c64, rt->stats->invalid_type_rcvd); return PTR_CAST(u_char, &c64); case VRRP_RFCv3_SNMP_STATS_ADDR_LIST_ERR: set_counter64(&c64, rt->stats->addr_list_err); return PTR_CAST(u_char, &c64); case VRRP_RFCv3_SNMP_STATS_PL_ERR: set_counter64(&c64, rt->stats->packet_len_err); return PTR_CAST(u_char, &c64); case VRRP_RFCv3_SNMP_STATS_ROW_DISC_TIME: // We don't "do" discontinuities *var_len = sizeof(ret); ret = 0; return PTR_CAST(u_char, &ret); case VRRP_RFCv3_SNMP_STATS_REFRESH_RATE: *var_len = sizeof(ret); ret = rt->adver_int / TIMER_CENTI_HZ * 10; /* milliseconds */ return PTR_CAST(u_char, &ret); } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return vrrp_rfcv3_snmp_statstable(vp, name, length, exact, var_len, write_method); return NULL; } static oid vrrp_rfcv3_oid[] = {VRRP_RFCv3_OID}; static struct variable7 vrrp_rfcv3_vars[] = { /* vrrpOperTable */ { VRRP_RFCv3_SNMP_OPER_MIP, ASN_OCTET_STR, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 3}}, { VRRP_RFCv3_SNMP_OPER_PIP, ASN_OCTET_STR, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 4}}, { VRRP_RFCv3_SNMP_OPER_VMAC, ASN_OCTET_STR, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 5}}, { VRRP_RFCv3_SNMP_OPER_STATE, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 6}}, { VRRP_RFCv3_SNMP_OPER_PRI, ASN_UNSIGNED, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 7}}, { VRRP_RFCv3_SNMP_OPER_ADDR_CNT, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 8}}, { VRRP_RFCv3_SNMP_OPER_ADVERT_INT, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 9}}, { VRRP_RFCv3_SNMP_OPER_PREEMPT, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 10}}, { VRRP_RFCv3_SNMP_OPER_ACCEPT, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 11}}, { VRRP_RFCv3_SNMP_OPER_VR_UPTIME, ASN_TIMETICKS, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 12}}, { VRRP_RFCv3_SNMP_OPER_ROW_STATUS, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_opertable, 5, {1, 1, 1, 1, 13}}, /* vrrpAssoIpAddrTable */ { VRRP_RFCv3_SNMP_ASSOC_IP_ADDR_ROW_STATUS, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_assoiptable, 5, {1, 1, 2, 1, 2}}, /* vrrpRouterStats */ { VRRP_RFCv3_SNMP_STATS_CHK_ERR, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_stats, 3, {1, 2, 1}}, { VRRP_RFCv3_SNMP_STATS_VER_ERR, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_stats, 3, {1, 2, 2}}, { VRRP_RFCv3_SNMP_STATS_VRID_ERR, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_stats, 3, {1, 2, 3}}, { VRRP_RFCv3_SNMP_STATS_DISC_TIME, ASN_TIMETICKS, RONLY, vrrp_rfcv3_snmp_stats, 3, {1, 2, 4}}, /* vrrpRouterStatsTable */ { VRRP_RFCv3_SNMP_STATS_MASTER, ASN_COUNTER, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 1}}, { VRRP_RFCv3_SNMP_STATS_MASTER_REASON, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 2}}, { VRRP_RFCv3_SNMP_STATS_ADV_RCVD, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 3}}, { VRRP_RFCv3_SNMP_STATS_ADV_INT_ERR, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 4}}, { VRRP_RFCv3_SNMP_STATS_TTL_ERR, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 5}}, { VRRP_RFCv3_SNMP_STATS_PROTO_ERR_REASON, ASN_INTEGER, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 6}}, { VRRP_RFCv3_SNMP_STATS_PRI_0_RCVD, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 7}}, { VRRP_RFCv3_SNMP_STATS_PRI_0_SENT, ASN_COUNTER64 , RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 8}}, { VRRP_RFCv3_SNMP_STATS_INV_TYPE_RCVD, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 9}}, { VRRP_RFCv3_SNMP_STATS_ADDR_LIST_ERR, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 10}}, { VRRP_RFCv3_SNMP_STATS_PL_ERR, ASN_COUNTER64, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 11}}, { VRRP_RFCv3_SNMP_STATS_ROW_DISC_TIME, ASN_TIMETICKS, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 12}}, { VRRP_RFCv3_SNMP_STATS_REFRESH_RATE, ASN_UNSIGNED, RONLY, vrrp_rfcv3_snmp_statstable, 5, {1, 2, 5, 1, 13}} }; void vrrp_rfcv3_snmp_new_master_notify(vrrp_t *vrrp) { /* OID of the notification vrrpTrapNewMaster */ oid notification_oid[] = { VRRP_RFCv3_NOTIFY_OID, 1 }; size_t notification_oid_len = OID_LENGTH(notification_oid); /* OID for snmpNotifyOID.0 */ oid objid_snmptrap[] = { SNMPTRAP_OID }; size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); /* OID for trap data vrrpOperMasterIPAddr */ oid masterip_oid[] = { VRRP_RFCv3_OID, 1, 1, 1, 1, 3, vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0, vrrp->vrid, vrrp->family == AF_INET ? 1 : 2 }; size_t masterip_oid_len = OID_LENGTH(masterip_oid); oid master_reason_oid[] = { VRRP_RFCv3_OID, 1, 2, 5, 1, 2, vrrp->ifp ? IF_BASE_INDEX(vrrp->ifp) : 0, vrrp->vrid, vrrp->family == AF_INET ? 1 : 2 }; size_t master_reason_oid_len = OID_LENGTH(master_reason_oid); uint32_t reason = vrrp->stats->master_reason; netsnmp_variable_list *notification_vars = NULL; if (!global_data->enable_traps || !global_data->enable_snmp_rfcv3) return; if (!suitable_for_rfc6527(vrrp)) return; /* snmpTrapOID */ snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, objid_snmptrap_len, ASN_OBJECT_ID, PTR_CAST(u_char, notification_oid), notification_oid_len * sizeof(oid)); /* vrrpInstanceName */ if (vrrp->family == AF_INET) snmp_varlist_add_variable(¬ification_vars, masterip_oid, masterip_oid_len, ASN_OCTET_STR, PTR_CAST2(u_char, struct sockaddr_in, &vrrp->saddr, sin_addr.s_addr), sizeof PTR_CAST(struct sockaddr_in, &vrrp->saddr)->sin_addr.s_addr); else snmp_varlist_add_variable(¬ification_vars, masterip_oid, masterip_oid_len, ASN_OCTET_STR, PTR_CAST2(u_char, struct sockaddr_in6, &vrrp->saddr, sin6_addr), sizeof PTR_CAST(struct sockaddr_in6, &vrrp->saddr)->sin6_addr); snmp_varlist_add_variable(¬ification_vars, master_reason_oid, master_reason_oid_len, ASN_INTEGER, PTR_CAST(u_char, &reason), sizeof(reason)); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Sending SNMP notification" " vrrpv3NotifyNewMaster, reason %" PRIu32 , vrrp->iname, reason); send_v2trap(notification_vars); snmp_free_varbind(notification_vars); } void vrrp_rfcv3_snmp_proto_err_notify(vrrp_t *vrrp) { /* OID of the notification vrrpTrapNewMaster */ oid notification_oid[] = { VRRP_RFCv3_NOTIFY_OID, 2 }; size_t notification_oid_len = OID_LENGTH(notification_oid); /* OID for snmpTrapOID.0 */ oid objid_snmptrap[] = { SNMPTRAP_OID }; size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); /* OID for notify data vrrpTrapProtoErrorType */ oid err_type_oid[] = { VRRP_RFCv3_OID, 1, 2, 5, 1, 6, vrrp->ifp ? IF_INDEX(vrrp->ifp) : 0, vrrp->vrid, vrrp->family == AF_INET ? 1 : 2 }; size_t err_type_oid_len = OID_LENGTH(err_type_oid); netsnmp_variable_list *notification_vars = NULL; if (!global_data->enable_traps || !global_data->enable_snmp_rfcv3) return; if (!suitable_for_rfc6527(vrrp)) return; /* snmpTrapOID */ snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, objid_snmptrap_len, ASN_OBJECT_ID, PTR_CAST(u_char, notification_oid), notification_oid_len * sizeof(oid)); /* vrrpProtoErrorType */ snmp_varlist_add_variable(¬ification_vars, err_type_oid, err_type_oid_len, ASN_INTEGER, PTR_CAST(u_char, &vrrp->stats->proto_err_reason), sizeof(vrrp->stats->proto_err_reason)); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Sending SNMP notification" " vrrpTrapProtoError" , vrrp->iname); send_v2trap(notification_vars); snmp_free_varbind(notification_vars); } #endif static bool vrrp_handles_global_oid(const data_t *global_data_in_use) { #ifdef _WITH_SNMP_VRRP_ if (global_data_in_use->enable_snmp_vrrp) { #ifdef _WITH_LVS_ if (!running_checker()) return true; #ifdef _WITH_SNMP_CHECKER_ if (!global_data_in_use->enable_snmp_checker) return true; #endif #else return true; #endif } #endif return false; } void vrrp_snmp_agent_init(const char *snmp_socket_name) { if (snmp_running) return; /* We let the check process handle the global OID if it is running and with snmp */ snmp_agent_init(snmp_socket_name, vrrp_handles_global_oid(global_data)); #ifdef _WITH_SNMP_VRRP_ if (global_data->enable_snmp_vrrp) snmp_register_mib(vrrp_oid, OID_LENGTH(vrrp_oid), "KEEPALIVED-VRRP", PTR_CAST(struct variable, vrrp_vars), sizeof(vrrp_vars[0]), sizeof(vrrp_vars)/sizeof(vrrp_vars[0])); #endif #ifdef _WITH_SNMP_RFCV2_ if (global_data->enable_snmp_rfcv2) snmp_register_mib(vrrp_rfcv2_oid, OID_LENGTH(vrrp_rfcv2_oid), "VRRP", PTR_CAST(struct variable, vrrp_rfcv2_vars), sizeof(vrrp_rfcv2_vars[0]), sizeof(vrrp_rfcv2_vars)/sizeof(vrrp_rfcv2_vars[0])); #endif #ifdef _WITH_SNMP_RFCV3_ if (global_data->enable_snmp_rfcv3) snmp_register_mib(vrrp_rfcv3_oid, OID_LENGTH(vrrp_rfcv3_oid), "VRRPV3", PTR_CAST(struct variable, vrrp_rfcv3_vars), sizeof(vrrp_rfcv3_vars[0]), sizeof(vrrp_rfcv3_vars)/sizeof(vrrp_rfcv3_vars[0])); #endif } void vrrp_snmp_agent_close(const data_t *global_data_in_use) { if (!snmp_running) return; #ifdef _WITH_SNMP_VRRP_ if (global_data_in_use->enable_snmp_vrrp) snmp_unregister_mib(vrrp_oid, OID_LENGTH(vrrp_oid)); #endif #ifdef _WITH_SNMP_RFCV2_ if (global_data_in_use->enable_snmp_rfcv2) snmp_unregister_mib(vrrp_rfcv2_oid, OID_LENGTH(vrrp_rfcv2_oid)); #endif #ifdef _WITH_SNMP_RFCV3_ if (global_data_in_use->enable_snmp_rfcv3) snmp_unregister_mib(vrrp_rfcv3_oid, OID_LENGTH(vrrp_rfcv3_oid)); #endif snmp_agent_close(vrrp_handles_global_oid(global_data_in_use)); } keepalived-2.3.3/keepalived/vrrp/vrrp_ipaddress.c0000664000175000017500000006151514707732645015637 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: NETLINK IPv4 address manipulation. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" /* Global include */ #include #include #include /* local include */ #include "vrrp_ipaddress.h" #include "vrrp.h" #include "keepalived_netlink.h" #include "vrrp_data.h" #include "logger.h" #include "utils.h" #include "bitops.h" #include "global_data.h" #include "rttables.h" #include "memory.h" #include "parser.h" #ifdef _WITH_FIREWALL_ #include "vrrp_firewall.h" #endif #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) { static char addr_str[IPADDRESSTOS_BUF_LEN]; char *end; if (!buf) buf = addr_str; if (IP_IS6(ip_addr)) inet_ntop(AF_INET6, &ip_addr->u.sin6_addr, buf, INET6_ADDRSTRLEN); else inet_ntop(AF_INET, &ip_addr->u.sin.sin_addr, buf, INET_ADDRSTRLEN); if ((ip_addr->ifa.ifa_family == AF_INET && ip_addr->ifa.ifa_prefixlen != 32 ) || (ip_addr->ifa.ifa_family == AF_INET6 && ip_addr->ifa.ifa_prefixlen != 128 )) { end = buf + strlen(buf); snprintf(end, buf + IPADDRESSTOS_BUF_LEN - end, "/%u", ip_addr->ifa.ifa_prefixlen); } return buf; } bool compare_ipaddress(const ip_address_t *X, const ip_address_t *Y) { if (!X && !Y) return false; if (!X != !Y || X->ifa.ifa_family != Y->ifa.ifa_family) return true; if (X->ifa.ifa_prefixlen != Y->ifa.ifa_prefixlen || // We can't check ifp here and later. On a reload, has ifp been set up by now? // !X->ifp != !Y->ifp || #ifdef _HAVE_VRRP_VMAC_ X->use_vmac != Y->use_vmac || #endif X->ifa.ifa_scope != Y->ifa.ifa_scope) return true; if (X->ifp && #ifdef _HAVE_VRRP_VMAC_ X->ifp->base_ifp != Y->ifp->base_ifp #else X->ifp != Y->ifp #endif ) return true; if (!string_equal(X->label, Y->label)) return true; if (X->ifa.ifa_family == AF_INET6) return X->u.sin6_addr.s6_addr32[0] != Y->u.sin6_addr.s6_addr32[0] || X->u.sin6_addr.s6_addr32[1] != Y->u.sin6_addr.s6_addr32[1] || X->u.sin6_addr.s6_addr32[2] != Y->u.sin6_addr.s6_addr32[2] || X->u.sin6_addr.s6_addr32[3] != Y->u.sin6_addr.s6_addr32[3]; return X->u.sin.sin_addr.s_addr != Y->u.sin.sin_addr.s_addr; } /* Add/Delete IP address to a specific interface_t */ int netlink_ipaddress(ip_address_t *ip_addr, int cmd) { struct ifa_cacheinfo cinfo; int status = 1; struct { struct nlmsghdr n; struct ifaddrmsg ifa; char buf[256]; } req; #if HAVE_DECL_IFA_FLAGS uint32_t ifa_flags = 0; #else uint8_t ifa_flags = 0; #endif if (cmd == IPADDRESS_ADD) { /* We can't add the address if the interface doesn't exist */ if (!ip_addr->ifp->ifindex) { log_message(LOG_INFO, "Not adding address %s to %s since interface doesn't exist" , ipaddresstos(NULL, ip_addr), ip_addr->ifp->ifname); return -1; } /* Make sure the ifindex for the address is current */ ip_addr->ifa.ifa_index = ip_addr->ifp->ifindex; } else if (!ip_addr->ifp->ifindex) { /* The interface has been deleted, so there is no point deleting the address */ return 0; } else if (!ip_addr->ifa.ifa_index) ip_addr->ifa.ifa_index = ip_addr->ifp->ifindex; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifaddrmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = (cmd == IPADDRESS_DEL) ? RTM_DELADDR : RTM_NEWADDR; req.ifa = ip_addr->ifa; if (cmd == IPADDRESS_ADD) ifa_flags = ip_addr->flags; if (IP_IS6(ip_addr)) { if (cmd == IPADDRESS_ADD) { /* A preferred_lft of 0 marks an IPv6 address as deprecated (rfc3484) * in order to prevent using VRRP VIP as source address in * healthchecking use cases. */ if (ip_addr->preferred_lft != INFINITY_LIFE_TIME) { memset(&cinfo, 0, sizeof(cinfo)); cinfo.ifa_prefered = ip_addr->preferred_lft; cinfo.ifa_valid = INFINITY_LIFE_TIME; addattr_l(&req.n, sizeof(req), IFA_CACHEINFO, &cinfo, sizeof(cinfo)); } /* Disable, per VIP, Duplicate Address Detection algorithm (DAD). * Using the nodad flag has the following benefits: * * (1) The address becomes immediately usable after they're * configured. * (2) In the case of a temporary layer-2 / split-brain problem * we can avoid that the active VIP transitions into the * dadfailed phase and stays there forever - leaving us * without service. HA/VRRP setups have their own "DAD"-like * functionality, so it's not really needed from the IPv6 stack. */ if (!(ip_addr->flagmask & IFA_F_NODAD)) ifa_flags |= IFA_F_NODAD; } addattr_l(&req.n, sizeof(req), IFA_LOCAL, &ip_addr->u.sin6_addr, sizeof(ip_addr->u.sin6_addr)); } else { addattr_l(&req.n, sizeof(req), IFA_LOCAL, &ip_addr->u.sin.sin_addr, sizeof(ip_addr->u.sin.sin_addr)); if (cmd == IPADDRESS_ADD) { if (ip_addr->u.sin.sin_brd.s_addr) addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &ip_addr->u.sin.sin_brd, sizeof(ip_addr->u.sin.sin_brd)); } else { /* IPADDRESS_DEL */ addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &ip_addr->u.sin.sin_addr, sizeof(ip_addr->u.sin.sin_addr)); } } if (cmd == IPADDRESS_ADD) { #if HAVE_DECL_IFA_FLAGS if (ifa_flags) addattr32(&req.n, sizeof(req), IFA_FLAGS, ifa_flags); #else req.ifa.ifa_flags = ifa_flags; #endif if (ip_addr->label) addattr_l(&req.n, sizeof (req), IFA_LABEL, ip_addr->label, strlen(ip_addr->label) + 1); 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 * has been deleted, but we get the link status change message before the RTM_DELLINK message */ if (cmd == IPADDRESS_DEL && (((ip_addr->ifp->ifi_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) #ifdef _HAVE_VRRP_VMAC_ || ((IF_BASE_IFP(ip_addr->ifp)->ifi_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) #endif )) netlink_error_ignore = ENODEV; if (netlink_talk(&nl_cmd, &req.n) < 0) status = -1; netlink_error_ignore = 0; return status; } /* Add/Delete a list of IP addresses */ bool netlink_iplist(list_head_t *ip_list, int cmd, bool force) { ip_address_t *ip_addr; bool changed_entries = false; /* * If "--dont-release-vrrp" is set then try to release addresses * that may be there, even if we didn't set them. */ list_for_each_entry(ip_addr, ip_list, e_list) { if ((cmd == IPADDRESS_ADD && !ip_addr->set) || (cmd == IPADDRESS_DEL && (force || ip_addr->set || __test_bit(DONT_RELEASE_VRRP_BIT, &debug)))) { /* If we are removing addresses left over from previous run * and they don't exist, don't report an error */ if (force) netlink_error_ignore = ENODEV; if (netlink_ipaddress(ip_addr, cmd) > 0) { ip_addr->set = (cmd == IPADDRESS_ADD); changed_entries = true; } else ip_addr->set = false; } } return changed_entries; } /* IP address dump/allocation */ void free_ipaddress(ip_address_t *ip_addr) { FREE_PTR(ip_addr->label); list_del_init(&ip_addr->e_list); list_del_init(&ip_addr->garp_gna_list); FREE(ip_addr); } void free_ipaddress_list(list_head_t *l) { ip_address_t *ip_addr, *ip_addr_tmp; list_for_each_entry_safe(ip_addr, ip_addr_tmp, l, e_list) free_ipaddress(ip_addr); } void format_ipaddress(const ip_address_t *ip_addr, char *buf, size_t buf_len) { char peer[INET6_ADDRSTRLEN + 4]; /* allow for subnet */ char *buf_p = buf; char *buf_end = buf + buf_len; if (ip_addr->ifa.ifa_family == AF_UNSPEC) { snprintf(buf_p, buf_end - buf_p, "None"); return; } buf_p += snprintf(buf_p, buf_end - buf_p, "%s", ipaddresstos(NULL, ip_addr)); if (!IP_IS6(ip_addr) && ip_addr->u.sin.sin_brd.s_addr) { buf_p += snprintf(buf_p, buf_end - buf_p, " brd %s", inet_ntop2(ip_addr->u.sin.sin_brd.s_addr)); } buf_p += snprintf(buf_p, buf_end - buf_p, " dev %s", IF_NAME(ip_addr->ifp)); #ifdef _HAVE_VRRP_VMAC_ if (!ip_addr->ifp) buf_p += snprintf(buf_p, buf_end - buf_p, "@NOWHERE"); else if (ip_addr->ifp != ip_addr->ifp->base_ifp) buf_p += snprintf(buf_p, buf_end - buf_p, "@%s", ip_addr->ifp->base_ifp->ifname); if (ip_addr->use_vmac) buf_p += snprintf(buf_p, buf_end - buf_p, "%s" , " use_vmac"); #endif buf_p += snprintf(buf_p, buf_end - buf_p, " scope %s" , get_rttables_scope(ip_addr->ifa.ifa_scope)); if (ip_addr->label) buf_p += snprintf(buf_p, buf_end - buf_p, " label %s", ip_addr->label); if (ip_addr->have_peer) { inet_ntop(ip_addr->ifa.ifa_family, &ip_addr->peer, peer, sizeof(peer)); buf_p += snprintf(buf_p, buf_end - buf_p, " peer %s/%d" , peer, ip_addr->ifa.ifa_prefixlen); } if (ip_addr->flags & IFA_F_HOMEADDRESS) buf_p += snprintf(buf_p, buf_end - buf_p, " home"); if (ip_addr->flagmask & IFA_F_NODAD) buf_p += snprintf(buf_p, buf_end - buf_p, " -nodad"); #ifdef IFA_F_MANAGETEMPADDR /* Linux 3.14 */ if (ip_addr->flags & IFA_F_MANAGETEMPADDR) buf_p += snprintf(buf_p, buf_end - buf_p, " mngtmpaddr"); #endif #ifdef IFA_F_NOPREFIXROUTE /* Linux 3.14 */ if (ip_addr->flags & IFA_F_NOPREFIXROUTE) buf_p += snprintf(buf_p, buf_end - buf_p, " noprefixroute"); #endif #ifdef IFA_F_MCAUTOJOIN /* Linux 4.1 */ if (ip_addr->flags & IFA_F_MCAUTOJOIN) buf_p += snprintf(buf_p, buf_end - buf_p, " autojoin"); #endif if (ip_addr->dont_track) buf_p += snprintf(buf_p, buf_end - buf_p, "%s", " no_track"); if (ip_addr->track_group) buf_p += snprintf(buf_p, buf_end - buf_p, " track_group %s", ip_addr->track_group->gname); if (IP_IS6(ip_addr)) { if (ip_addr->preferred_lft == 0) buf_p += snprintf(buf_p, buf_end - buf_p, " deprecated"); else if (ip_addr->preferred_lft == INFINITY_LIFE_TIME) buf_p += snprintf(buf_p, buf_end - buf_p, " preferred_lft forever"); else buf_p += snprintf(buf_p, buf_end - buf_p, " preferred_lft %" PRIu32, ip_addr->preferred_lft); } if (ip_addr->set) buf_p += snprintf(buf_p, buf_end - buf_p, " set"); #ifdef _WITH_IPTABLES_ if (ip_addr->iptable_rule_set) buf_p += snprintf(buf_p, buf_end - buf_p, " iptable_set"); #endif #ifdef _WITH_NFTABLES_ if (ip_addr->nftable_rule_set) buf_p += snprintf(buf_p, buf_end - buf_p, " nftable_set"); #endif } void dump_ipaddress(FILE *fp, const ip_address_t *ip_addr) { char buf[256]; format_ipaddress(ip_addr, buf, sizeof(buf)); conf_write(fp, " %s", buf); } void dump_ipaddress_list(FILE *fp, const list_head_t *l) { ip_address_t *ip_addr; list_for_each_entry(ip_addr, l, e_list) dump_ipaddress(fp, ip_addr); } ip_address_t * parse_ipaddress(ip_address_t *ip_addr, const char *str, bool allow_subnet_mask) { ip_address_t *new = ip_addr; void *addr; const char *p; unsigned prefixlen; const char *str_dup = NULL; /* No ip address, allocate a brand new one */ if (!new) PMALLOC(new); /* Parse ip address */ new->ifa.ifa_family = (strchr(str, ':')) ? AF_INET6 : AF_INET; new->ifa.ifa_prefixlen = (IP_IS6(new)) ? 128 : 32; if (allow_subnet_mask) p = strchr(str, '/'); else p = NULL; if (p) { if (!read_unsigned(p + 1, &prefixlen, 0, new->ifa.ifa_prefixlen, true)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid address prefix len %s for address %s - using %d", p + 1, str, new->ifa.ifa_prefixlen); else new->ifa.ifa_prefixlen = prefixlen; str_dup = STRNDUP(str, p - str); } addr = (IP_IS6(new)) ? (void *) &new->u.sin6_addr : (void *) &new->u.sin.sin_addr; if (!inet_pton(IP_FAMILY(new), str_dup ? str_dup : str, addr)) { report_config_error(CONFIG_GENERAL_ERROR, "VRRP parsed invalid IP %s. skipping IP...", str); if (!ip_addr) FREE(new); new = NULL; } /* Release dup'd string */ if (str_dup) FREE_CONST(str_dup); return new; } ip_address_t * parse_route(const char *str) { ip_address_t *new; PMALLOC(new); /* Handle the specials */ if (!strcmp(str, "default") || !strcmp(str, "any") || !strcmp(str, "all")) { new->ifa.ifa_family = AF_UNSPEC; return new; } /* Maintained for backward compatibility v2.0.7 and earlier */ if (!strcmp(str, "default6")) { log_message(LOG_INFO, "'default6' is deprecated - please replace with 'inet6 default'"); new->ifa.ifa_family = AF_INET6; return new; } if (!parse_ipaddress(new, str, true)) { FREE(new); return NULL; } return new; } ip_address_t * alloc_ipaddress(const vector_t *strvec, bool static_addr) { /* The way this works is slightly strange. * * We don't set the interface for the address unless dev DEVNAME is specified, * in case a VMAC is added later. When the complete configuration is checked, * if the ifindex is 0, then it will be set to the interface of the * vrrp_instance (VMAC or physical interface). */ ip_address_t *new; interface_t *ifp_local; const char *str; unsigned int i = 0, addr_idx = 0; uint8_t scope; bool param_avail; bool param_missing = false; const char *param; ip_address_t peer = { .ifa.ifa_family = AF_UNSPEC }; int brd_len = 0; uint32_t mask; bool have_broadcast = false; unsigned preferred_lft; bool preferred_lft_set = false; PMALLOC(new); if (!new) { log_message(LOG_INFO, "Unable to allocate new ip_address"); return NULL; } INIT_LIST_HEAD(&new->e_list); INIT_LIST_HEAD(&new->garp_gna_list); /* We expect the address first */ if (!parse_ipaddress(new, strvec_slot(strvec, 0), true)) { FREE(new); return NULL; } addr_idx = i++; /* FMT parse */ while (i < vector_size(strvec)) { str = strvec_slot(strvec, i); /* cmd parsing */ param_avail = (vector_size(strvec) >= i+2); if (!strcmp(str, "dev")) { if (!param_avail) { param_missing = true; break; } if (new->ifp) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify ipaddress device more than once for %s", strvec_slot(strvec, addr_idx)); FREE(new); return NULL; } if (!(ifp_local = if_get_by_ifname(strvec_slot(strvec, ++i), IF_CREATE_IF_DYNAMIC))) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - interface %s for ip address %s doesn't exist", strvec_slot(strvec, i), strvec_slot(strvec, addr_idx)); FREE(new); return NULL; } new->ifp = ifp_local; } else if (!strcmp(str, "scope")) { if (!param_avail) { param_missing = true; break; } if (!find_rttables_scope(strvec_slot(strvec, ++i), &scope)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid scope '%s' specified for %s - ignoring", strvec_slot(strvec,i), strvec_slot(strvec, addr_idx)); else new->ifa.ifa_scope = scope; } else if (!strcmp(str, "broadcast") || !strcmp(str, "brd")) { if (!param_avail) { param_missing = true; break; } if (IP_IS6(new)) { report_config_error(CONFIG_GENERAL_ERROR, "VRRP is trying to assign a broadcast %s to the IPv6 address %s !!?? " "WTF... skipping VIP..." , strvec_slot(strvec, i), strvec_slot(strvec, addr_idx)); FREE(new); return NULL; } have_broadcast = true; param = strvec_slot(strvec, ++i); if (!strcmp(param, "-")) brd_len = -2; else if (!strcmp(param, "+")) brd_len = -1; else if (!inet_pton(AF_INET, param, &new->u.sin.sin_brd)) { report_config_error(CONFIG_GENERAL_ERROR, "VRRP is trying to assign invalid broadcast %s. " "skipping VIP...", strvec_slot(strvec, i)); FREE(new); return NULL; } } else if (!strcmp(str, "label")) { if (!param_avail) { param_missing = true; break; } param = strvec_slot(strvec, ++i); if (strlen(param) >= IFNAMSIZ) { report_config_error(CONFIG_GENERAL_ERROR, "Address label %s is longer than maximum length %d - removing address", param, IFNAMSIZ - 1); FREE(new); return NULL; } new->label = MALLOC(strlen(param) + 1); strcpy(new->label, param); } else if (!strcmp(str, "peer")) { if (!param_avail) { param_missing = true; break; } i++; if (new->have_peer) { report_config_error(CONFIG_GENERAL_ERROR, "Peer %s - another peer has already been specified", strvec_slot(strvec, i)); continue; } if (!parse_ipaddress(&peer, strvec_slot(strvec,i), false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid peer address %s", strvec_slot(strvec, i)); else if (peer.ifa.ifa_family != new->ifa.ifa_family) report_config_error(CONFIG_GENERAL_ERROR, "Peer address %s does not match address family", strvec_slot(strvec, i)); else { if ((new->ifa.ifa_family == AF_INET6 && new->ifa.ifa_prefixlen != 128) || (new->ifa.ifa_family == AF_INET && new->ifa.ifa_prefixlen != 32)) report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify address prefix when specifying peer address - ignoring"); new->have_peer = true; new->ifa.ifa_prefixlen = peer.ifa.ifa_prefixlen; if (new->ifa.ifa_family == AF_INET6) new->peer.sin6_addr = peer.u.sin6_addr; else new->peer.sin_addr = peer.u.sin.sin_addr; } } else if (!strcmp(str, "home")) { new->flags |= IFA_F_HOMEADDRESS; new->flagmask |= IFA_F_HOMEADDRESS; } else if (!strcmp(str, "-nodad")) { new->flagmask |= IFA_F_NODAD; #ifdef IFA_F_MANAGETEMPADDR /* Linux 3.14 */ } else if (!strcmp(str, "mngtmpaddr")) { new->flags |= IFA_F_MANAGETEMPADDR; new->flagmask |= IFA_F_MANAGETEMPADDR; #endif #ifdef IFA_F_NOPREFIXROUTE /* Linux 3.14 */ } else if (!strcmp(str, "noprefixroute")) { new->flags |= IFA_F_NOPREFIXROUTE; new->flagmask |= IFA_F_NOPREFIXROUTE; #endif #ifdef IFA_F_MCAUTOJOIN /* Linux 4.1 */ } else if (!strcmp(str, "autojoin")) { new->flags |= IFA_F_MCAUTOJOIN; new->flagmask |= IFA_F_MCAUTOJOIN; #endif } else if (!strcmp(str, "no_track")) { new->dont_track = true; } else if (!strcmp(str, "preferred_lft")) { if (!param_avail) { param_missing = true; break; } i++; if (!strcmp(strvec_slot(strvec, i), "forever")) { new->preferred_lft = INFINITY_LIFE_TIME; preferred_lft_set = true; } else if (read_unsigned_strvec(strvec, i, &preferred_lft, 0, UINT32_MAX, true)) { new->preferred_lft = (uint32_t)preferred_lft; preferred_lft_set = true; } else report_config_error(CONFIG_GENERAL_ERROR, "preferred_lft %s is invalid", strvec_slot(strvec, i)); } else if (static_addr && !strcmp(str, "track_group")) { if (!param_avail) { param_missing = true; break; } i++; if (new->track_group) { report_config_error(CONFIG_GENERAL_ERROR, "track_group %s is a duplicate", strvec_slot(strvec, i)); break; } if (!(new->track_group = static_track_group_find(strvec_slot(strvec, i)))) report_config_error(CONFIG_GENERAL_ERROR, "track_group %s not found", strvec_slot(strvec, i)); } else if (!static_addr && !strcmp(str, "use_vmac")) { new->use_vmac = true; } else report_config_error(CONFIG_GENERAL_ERROR, "Unknown configuration entry '%s' for ip address - ignoring", str); i++; } /* Check if there was a missing parameter for a keyword */ if (param_missing) { report_config_error(CONFIG_GENERAL_ERROR, "No %s parameter specified for %s", str, strvec_slot(strvec, addr_idx)); FREE(new); return NULL; } /* Set the broadcast address if necessary */ if (have_broadcast && new->have_peer) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify broadcast and peer addresses - ignoring broadcast address"); new->u.sin.sin_brd.s_addr = 0; } else if (brd_len < 0 && new->ifa.ifa_prefixlen <= 30) { new->u.sin.sin_brd = (new->have_peer) ? new->peer.sin_addr : new->u.sin.sin_addr; mask = 0xffffffffU >> new->ifa.ifa_prefixlen; mask = htonl(mask); if (brd_len == -1) /* '+' */ new->u.sin.sin_brd.s_addr |= mask; else new->u.sin.sin_brd.s_addr &= ~mask; } else if (brd_len < 0) report_config_error(CONFIG_GENERAL_ERROR, "Address prefix length %d too long for broadcast", new->ifa.ifa_prefixlen); if (static_addr && !new->ifp) { new->ifp = get_default_if(); if (!new->ifp) { report_config_error(CONFIG_FATAL, "Static address %s requires either an interface" " or default interface must exist" , strvec_slot(strvec, addr_idx)); FREE(new); return NULL; } } if (new->ifa.ifa_family == AF_INET6) { if (new->ifa.ifa_scope) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify scope for IPv6 addresses (%s) - ignoring scope", strvec_slot(strvec, addr_idx)); new->ifa.ifa_scope = 0; } if (new->label) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify label for IPv6 addresses (%s) - ignoring label", strvec_slot(strvec, addr_idx)); FREE(new->label); new->label = NULL; } if (!preferred_lft_set) { /* Set the old defaults if preferred_lft not set */ if (new->ifa.ifa_prefixlen == 128) new->preferred_lft = 0; else new->preferred_lft = INFINITY_LIFE_TIME; } } if (new->track_group && !new->ifp) { report_config_error(CONFIG_GENERAL_ERROR, "Static route cannot have track_group if interface not specified"); new->track_group = NULL; } #if 0 if (!new->ifp && new->use_vmac) { report_config_error(CONFIG_GENERAL_ERROR, "use_vmac for a address requires an interface"); new->use_vmac = false; } #endif return new; } /* Find an address in a list */ static bool address_exist(vrrp_t *vrrp, ip_address_t *ip_addr) { ip_address_t *ipaddr; char addr_str[INET6_ADDRSTRLEN]; void *addr; list_head_t *vip_list; /* If the following check isn't made, we get lots of compiler warnings */ if (!ip_addr) return true; for (vip_list = &vrrp->vip; vip_list; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL ) { list_for_each_entry(ipaddr, vip_list, e_list) { if (!compare_ipaddress(ipaddr, ip_addr)) { ipaddr->set = ip_addr->set; #ifdef _WITH_IPTABLES_ ipaddr->iptable_rule_set = ip_addr->iptable_rule_set; #endif #ifdef _WITH_NFTABLES_ ipaddr->nftable_rule_set = ip_addr->nftable_rule_set; #endif ipaddr->ifa.ifa_index = ip_addr->ifa.ifa_index; return true; } } } addr = (IP_IS6(ip_addr)) ? (void *) &ip_addr->u.sin6_addr : (void *) &ip_addr->u.sin.sin_addr; inet_ntop(IP_FAMILY(ip_addr), addr, addr_str, INET6_ADDRSTRLEN); log_message(LOG_INFO, "(%s) ip address %s/%d dev %s, no longer exist" , vrrp->iname , addr_str , ip_addr->ifa.ifa_prefixlen , ip_addr->ifp->ifname); return false; } /* Clear diff addresses */ void get_diff_address(vrrp_t *old, vrrp_t *new, list_head_t *old_addr) { ip_address_t *ip_addr, *ip_addr_tmp; list_head_t *vip_list; /* No addresses in previous conf */ if (list_empty(&old->vip) && list_empty(&old->evip)) return; for (vip_list = &old->vip; vip_list; vip_list = vip_list == &old->vip ? &old->evip : NULL ) { list_for_each_entry_safe(ip_addr, ip_addr_tmp, vip_list, e_list) { if (ip_addr->set && !address_exist(new, ip_addr)) { list_del_init(&ip_addr->e_list); list_add_tail(&ip_addr->e_list, old_addr); } } } } /* Clear diff addresses */ void clear_address_list(list_head_t *delete_addr, #ifndef _WITH_FIREWALL_ __attribute__((unused)) #endif bool remove_from_firewall) { /* No addresses to delete */ if (list_empty(delete_addr)) return; /* All addresses removed */ netlink_iplist(delete_addr, IPADDRESS_DEL, false); #ifdef _WITH_FIREWALL_ if (remove_from_firewall) firewall_remove_rule_to_iplist(delete_addr); #endif } /* Clear static ip address */ void clear_diff_static_addresses(void) { LIST_HEAD_INITIALIZE(remove_addr); vrrp_t old = {0}; vrrp_t new = {0}; list_copy(&old.vip, &old_vrrp_data->static_addresses); list_copy(&new.vip, &vrrp_data->static_addresses); INIT_LIST_HEAD(&old.evip); INIT_LIST_HEAD(&new.evip); get_diff_address(&old, &new, &remove_addr); list_copy(&old_vrrp_data->static_addresses, &old.vip); list_copy(&vrrp_data->static_addresses, &new.vip); clear_address_list(&remove_addr, false); free_ipaddress_list(&remove_addr); } void reinstate_static_address(ip_address_t *ip_addr) { char buf[256]; ip_addr->set = (netlink_ipaddress(ip_addr, IPADDRESS_ADD) > 0); 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 } keepalived-2.3.3/keepalived/vrrp/vrrp_ndisc.c0000664000175000017500000002230514705536371014747 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: IPv6 Neighbour Discovery part. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" /* system includes */ #include #ifdef _HAVE_LINUX_IF_ETHER_H_COLLISION_ #include #endif #include #include #include #include #include #include #include #include #include /* local includes */ #include "vrrp_ndisc.h" #include "logger.h" #include "utils.h" #include "vrrp_if_config.h" #include "vrrp_scheduler.h" #include "vrrp_arp.h" #include "bitops.h" /* static vars */ static int ndisc_fd = -1; /* * See RFC 4391(Section 4 ) and RFC 4392 for details * This is modified by the IPoIB driver to add the P_key */ static unsigned char ipv6_bcast_addr[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x60, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }; /* * Neighbour Advertisement sending routine. */ static void ndisc_send_na(ip_address_t *ipaddress, struct iovec *iov, int iovlen) { struct sockaddr_large_ll sll; ssize_t len; char addr_str[INET6_ADDRSTRLEN] = ""; interface_t *ifp = ipaddress->ifp; struct msghdr msg = { .msg_iov = iov, .msg_iovlen = iovlen }; /* Build the dst device */ memset(&sll, 0, sizeof (sll)); sll.sll_family = AF_PACKET; sll.sll_ifindex = (int)IF_INDEX(ifp); /* The values in sll_ha_type, sll_addr and sll_halen appear to be ignored */ sll.sll_hatype = ifp->hw_type; sll.sll_halen = ifp->hw_addr_len; sll.sll_protocol = htons(ETH_P_IPV6); memcpy(sll.sll_addr, IF_HWADDR(ifp), ifp->hw_addr_len); msg.msg_name = &sll; msg.msg_namelen = sizeof(sll); if (__test_bit(LOG_DETAIL_BIT, &debug)) { inet_ntop(AF_INET6, &ipaddress->u.sin6_addr, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Sending unsolicited Neighbour Advert on %s for %s", IF_NAME(ifp), addr_str); } /* Send packet */ len = sendmsg(ndisc_fd, &msg, 0); if (len < 0) { if (!addr_str[0]) inet_ntop(AF_INET6, &ipaddress->u.sin6_addr, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Error %d sending ndisc unsolicited neighbour advert on %s for %s", errno, IF_NAME(ifp), addr_str); } } /* * ICMPv6 Checksumming. */ static __sum16 ndisc_icmp6_cksum(const struct ip6hdr *ip6, struct iovec *iov, int iovcnt) { size_t i; int j; size_t len; const uint16_t *sp; uint32_t sum; union { struct { struct in6_addr ph_src; struct in6_addr ph_dst; uint32_t ph_len; uint8_t ph_zero[3]; uint8_t ph_nxt; } ph; uint16_t pa[20]; } phu; /* pseudo-header */ memset(&phu, 0, sizeof(phu)); memcpy(&phu.ph.ph_src, &ip6->saddr, sizeof(struct in6_addr)); memcpy(&phu.ph.ph_dst, &ip6->daddr, sizeof(struct in6_addr)); phu.ph.ph_len = 0; phu.ph.ph_nxt = IPPROTO_ICMPV6; sum = 0; for (j = 0; j < iovcnt; j++) { sp = PTR_CAST_CONST(uint16_t, iov[j].iov_base); len = iov[j].iov_len; for (i = 1; i < len; i += 2) sum += *sp++; if (len & 1) sum += htons((*PTR_CAST_CONST(uint8_t, sp)) << 8); phu.ph.ph_len += len; } phu.ph.ph_len = htons(phu.ph.ph_len); for (i = 0; i < sizeof(phu.pa) / sizeof(phu.pa[0]); i++) sum += phu.pa[i]; while (sum > 0xffff) sum = (sum & 0xffff) + (sum >> 16); return ~sum & 0xffff; } /* * Build an unsolicited Neighbour Advertisement. * As explained in rfc4861.4.4, a node sends unsolicited * Neighbor Advertisements in order to (unreliably) propagate * new information quickly. */ void ndisc_send_unsolicited_na_immediate(interface_t *ifp, ip_address_t *ipaddress) { struct ether_header eth = { .ether_type = htons(ETHERTYPE_IPV6) }; ipoib_hdr_t ipoib = { .proto = htons(ETHERTYPE_IPV6) }; struct ip6hdr ip6h = { .version = 6, .nexthdr = IPPROTO_ICMPV6, .hop_limit = NDISC_HOPLIMIT }; struct nd_neighbor_advert ndh = { .nd_na_type = ND_NEIGHBOR_ADVERT }; struct nd_opt_hdr nd_opt_h = { .nd_opt_type = ND_OPT_TARGET_LINKADDR }; uint8_t nd_opt_h_pad[2] = { 0, 0 }; struct iovec iov[7]; unsigned num_iov; unsigned icmp6_iov; /* For Infiniband see vrrp_arp.c and RFC2461 4.4, RFC4391, RFC4392 9.3 and * https://datatracker.ietf.org/doc/html/draft-kashyap-ipoib-ipv6-over-infiniband-00 */ if (ifp->hw_type == ARPHRD_INFINIBAND) { iov[0].iov_base = &ipv6_bcast_addr; iov[0].iov_len = sizeof(ipv6_bcast_addr); iov[1].iov_base = &ipoib; iov[1].iov_len = sizeof(ipoib); num_iov = 2; } else { /* Ethernet header: * Destination ethernet address MUST use specific address Mapping * as specified in rfc2464.7 Address Mapping for */ eth.ether_dhost[0] = eth.ether_dhost[1] = 0x33; eth.ether_dhost[5] = 1; memcpy(eth.ether_shost, ipaddress->ifp->hw_addr, ETH_ALEN); iov[0].iov_base = ð iov[0].iov_len = sizeof(eth); num_iov = 1; } /* IPv6 Header */ memcpy(&ip6h.saddr, &ipaddress->u.sin6_addr, sizeof(struct in6_addr)); ip6h.daddr.s6_addr16[0] = htons(0xff02); ip6h.daddr.s6_addr16[7] = htons(1); iov[num_iov].iov_base = &ip6h; iov[num_iov].iov_len = sizeof(ip6h); num_iov++; /* ICMPv6 Header */ /* Set the router flag if necessary. We recheck each interface if not * checked in the last 5 seconds. */ if (timer_cmp_now_diff(ifp->last_gna_router_check, 5 * TIMER_HZ)) set_ipv6_forwarding(ifp); if (ifp->gna_router) ndh.nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; /* Override flag is set to indicate that the advertisement * should override an existing cache entry and update the * cached link-layer address. */ ndh.nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE; ndh.nd_na_target = ipaddress->u.sin6_addr; iov[num_iov].iov_base = &ndh; iov[num_iov].iov_len = sizeof(ndh); icmp6_iov = num_iov; num_iov++; /* NDISC Option header */ iov[num_iov].iov_base = &nd_opt_h; iov[num_iov].iov_len = sizeof(nd_opt_h); num_iov++; if (ifp->hw_type == ARPHRD_INFINIBAND) { nd_opt_h.nd_opt_len = 3; iov[num_iov].iov_base = nd_opt_h_pad; iov[num_iov].iov_len = sizeof(nd_opt_h_pad); num_iov++; } else nd_opt_h.nd_opt_len = 1; /* MAC address */ iov[num_iov].iov_base = ipaddress->ifp->hw_addr; iov[num_iov].iov_len = ipaddress->ifp->hw_addr_len; num_iov++; ip6h.payload_len = htons(sizeof(struct nd_neighbor_advert) + nd_opt_h.nd_opt_len * 8); /* Compute checksum - ICMP6 header onwards*/ ndh.nd_na_hdr.icmp6_cksum = ndisc_icmp6_cksum(&ip6h, &iov[icmp6_iov], num_iov - icmp6_iov); /* Send the neighbor advertisement message */ ndisc_send_na(ipaddress, iov, num_iov); /* If we have to delay between sending NAs, note the next time we can */ if (ifp->garp_delay && ifp->garp_delay->have_gna_interval) ifp->garp_delay->gna_next_time = timer_add_now(ifp->garp_delay->gna_interval); } static void queue_ndisc(interface_t *ifp, ip_address_t *ipaddress) { ipaddress->garp_gna_pending = 1; if (list_empty(&ifp->garp_delay->gna_list)) thread_add_timer(master, vrrp_gna_thread, ifp, timer_long(timer_sub_now(ifp->garp_delay->gna_next_time))); list_add_tail(&ipaddress->garp_gna_list, &ifp->garp_delay->gna_list); } void ndisc_send_unsolicited_na(ip_address_t *ipaddress, unsigned rep) { interface_t *ifp = IF_BASE_IFP(ipaddress->ifp); /* If the interface doesn't support NDISC, don't try sending */ if (ifp->ifi_flags & IFF_NOARP) return; if (ipaddress->garp_gna_pending) { if (ipaddress->garp_gna_pending < rep) ipaddress->garp_gna_pending++; return; } set_time_now(); /* Do we need to delay sending the ndisc? */ if (ifp->garp_delay && ifp->garp_delay->have_gna_interval && ifp->garp_delay->gna_next_time.tv_sec && timercmp(&time_now, &ifp->garp_delay->gna_next_time, <)) queue_ndisc(ifp, ipaddress); else ndisc_send_unsolicited_na_immediate(ifp, ipaddress); } /* * Neighbour Discovery init/close */ bool ndisc_init(void) { if (ndisc_fd != -1) return true; /* Create the socket descriptor */ ndisc_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, htons(ETH_P_IPV6)); if (ndisc_fd < 0) { log_message(LOG_INFO, "Error %d while registering gratuitous NDISC shared channel", errno); return (errno != EAFNOSUPPORT && errno != EPERM); } if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Registering gratuitous NDISC shared channel"); /* We don't want to receive any data on this socket */ if_setsockopt_no_receive(&ndisc_fd); return true; } void ndisc_close(void) { if (ndisc_fd != -1) { close(ndisc_fd); ndisc_fd = -1; } } keepalived-2.3.3/keepalived/vrrp/vrrp_parser.c0000664000175000017500000021776114756615757015173 /* * Soft: Keepalived is a failover program for the LVS project * . 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, * * 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, */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "vrrp_parser.h" #include "logger.h" #include "parser.h" #include "bitops.h" #include "utils.h" #include "main.h" #include "global_data.h" #include "global_parser.h" #include "rttables.h" #include "vrrp_data.h" #include "vrrp_ipaddress.h" #include "vrrp_sync.h" #include "vrrp_track.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_vmac.h" #endif #include "vrrp_static_track.h" #ifdef _WITH_LVS_ #include "check_parser.h" #endif #ifdef _WITH_BFD_ #include "bfd_parser.h" #endif #include "track_file.h" #ifdef _WITH_TRACK_PROCESS_ #include "track_process.h" #endif enum process_delay { PROCESS_DELAY, PROCESS_TERMINATE_DELAY, PROCESS_FORK_DELAY, }; static bool script_user_set; static bool remove_script; static static_track_group_t *current_stg; vrrp_sgroup_t *current_vsyncg; static garp_delay_t *current_ggd; vrrp_t *current_vrrp; static vrrp_script_t *current_vscr; #ifdef _WITH_TRACK_PROCESS_ static vrrp_tracked_process_t *current_tp; #endif static unsigned cur_aggregation_group; /* track groups for static items */ static void static_track_group_handler(const vector_t *strvec) { static_track_group_t *tg; const char *gname; if (!strvec) return; if (vector_count(strvec) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "track_group must have a name - skipping"); skip_block(true); return; } gname = strvec_slot(strvec, 1); /* check group doesn't already exist */ list_for_each_entry(tg, &vrrp_data->static_track_groups, e_list) { if (!strcmp(gname,tg->gname)) { report_config_error(CONFIG_GENERAL_ERROR, "track_group %s already defined" , gname); skip_block(true); return; } } current_stg = alloc_static_track_group(gname); } static void static_track_group_group_handler(const vector_t *strvec) { if (current_stg->iname) { report_config_error(CONFIG_GENERAL_ERROR, "Group list already specified for sync group %s", current_stg->gname); skip_block(true); return; } current_stg->iname = read_value_block(strvec); if (!current_stg->iname) report_config_error(CONFIG_GENERAL_ERROR, "Warning - track group %s has empty group block", current_stg->gname); } static void static_track_group_end_handler(void) { if (!current_stg->iname) { report_config_error(CONFIG_GENERAL_ERROR, "Static track group %s has no members - removing", current_stg->gname); free_static_track_group(current_stg); return; } list_add_tail(¤t_stg->e_list, &vrrp_data->static_track_groups); } /* Static addresses handler */ static void static_addresses_handler(const vector_t *strvec) { global_data->have_vrrp_config = true; if (!strvec) return; alloc_value_block(alloc_saddress, strvec); } /* Static routes handler */ static void static_routes_handler(const vector_t *strvec) { global_data->have_vrrp_config = true; if (!strvec) return; alloc_value_block(alloc_sroute, strvec); } /* Static rules handler */ static void static_rules_handler(const vector_t *strvec) { global_data->have_vrrp_config = true; if (!strvec) return; alloc_value_block(alloc_srule, strvec); } #ifdef _WITH_LINKBEAT_ static void alloc_linkbeat_interface(const vector_t *strvec) { interface_t *ifp; int lb_type = 0; if (!(ifp = if_get_by_ifname(vector_slot(strvec, 0), global_data->dynamic_interfaces))) { report_config_error(CONFIG_FATAL, "unknown interface %s specified for linkbeat interface", strvec_slot(strvec, 0)); return; } #ifdef _HAVE_VRRP_VMAC_ /* netlink messages work for vmacs */ if (IS_MAC_IP_VLAN(ifp)) { log_message(LOG_INFO, "(%s): linkbeat not supported for vmacs since netlink works", ifp->ifname); return; } #endif if (vector_size(strvec) > 1) { if (!strcmp(strvec_slot(strvec, 1), "MII")) lb_type = LB_MII; else if (!strcmp(strvec_slot(strvec, 1), "ETHTOOL")) lb_type = LB_ETHTOOL; else if (!strcmp(strvec_slot(strvec, 1), "IOCTL")) lb_type = LB_IOCTL; if (!lb_type || vector_size(strvec) > 2) report_config_error(CONFIG_GENERAL_ERROR, "extra characters %s in linkbeat interface", strvec_slot(strvec, 1)); } ifp->linkbeat_use_polling = true; ifp->lb_type = lb_type; } static void linkbeat_interfaces_handler(const vector_t *strvec) { if (!strvec) return; alloc_value_block(alloc_linkbeat_interface, strvec); } #endif /* VRRP handlers */ static void vrrp_sync_group_handler(const vector_t *strvec) { vrrp_sgroup_t *sgroup; const char *gname; if (!strvec) return; if (vector_count(strvec) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_sync_group must have a name - skipping"); skip_block(true); return; } gname = strvec_slot(strvec, 1); /* check group doesn't already exist */ list_for_each_entry(sgroup, &vrrp_data->vrrp_sync_group, e_list) { if (!strcmp(gname, sgroup->gname)) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp sync group %s already defined", gname); skip_block(true); return; } } current_vsyncg = alloc_vrrp_sync_group(gname); } static void vrrp_group_handler(const vector_t *strvec) { if (current_vsyncg->iname) { report_config_error(CONFIG_GENERAL_ERROR, "Group list already specified for sync group %s" , current_vsyncg->gname); skip_block(true); return; } current_vsyncg->iname = read_value_block(strvec); if (!current_vsyncg->iname) report_config_error(CONFIG_GENERAL_ERROR, "Warning - sync group %s has empty group block" , current_vsyncg->gname); } static void vrrp_group_track_if_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_group_track_if, strvec); } static void vrrp_group_track_scr_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_group_track_script, strvec); } static void vrrp_group_track_file_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_group_track_file, strvec); } #ifdef _WITH_TRACK_PROCESS_ static void vrrp_group_track_process_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_group_track_process, strvec); } #endif #if defined _WITH_BFD_ static void vrrp_group_track_bfd_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_group_track_bfd, strvec); } #endif static void vrrp_sync_group_end_handler(void) { if (!current_vsyncg->iname) { report_config_error(CONFIG_GENERAL_ERROR, "Sync group %s has no members - removing" , current_vsyncg->gname); free_sync_group(current_vsyncg); return; } list_add_tail(¤t_vsyncg->e_list, &vrrp_data->vrrp_sync_group); } static inline notify_script_t* set_vrrp_notify_script(__attribute__((unused)) const vector_t *strvec, int extra_params) { return notify_script_init(extra_params, "notify"); } static void vrrp_gnotify_backup_handler(const vector_t *strvec) { if (current_vsyncg->script_backup) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp group %s: notify_backup script already specified - ignoring %s", current_vsyncg->gname, strvec_slot(strvec,1)); return; } current_vsyncg->script_backup = set_vrrp_notify_script(strvec, 0); current_vsyncg->notify_exec = true; } static void vrrp_gnotify_master_handler(const vector_t *strvec) { if (current_vsyncg->script_master) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp group %s: notify_master script already specified - ignoring %s", current_vsyncg->gname, strvec_slot(strvec,1)); return; } current_vsyncg->script_master = set_vrrp_notify_script(strvec, 0); current_vsyncg->notify_exec = true; } static void vrrp_gnotify_fault_handler(const vector_t *strvec) { if (current_vsyncg->script_fault) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp group %s: notify_fault script already specified - ignoring %s", current_vsyncg->gname, strvec_slot(strvec,1)); return; } current_vsyncg->script_fault = set_vrrp_notify_script(strvec, 0); current_vsyncg->notify_exec = true; } static void vrrp_gnotify_stop_handler(const vector_t *strvec) { if (current_vsyncg->script_stop) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp group %s: notify_stop script already specified - ignoring %s", current_vsyncg->gname, strvec_slot(strvec,1)); return; } current_vsyncg->script_stop = set_vrrp_notify_script(strvec, 0); current_vsyncg->notify_exec = true; } static void vrrp_gnotify_handler(const vector_t *strvec) { if (current_vsyncg->script) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp group %s: notify script already specified - ignoring %s", current_vsyncg->gname, strvec_slot(strvec,1)); return; } current_vsyncg->script = set_vrrp_notify_script(strvec, 4); current_vsyncg->notify_exec = true; } static void vrrp_gsmtp_handler(__attribute__((unused)) const vector_t *strvec) { 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 vrrp_group smtp_alert parameter %s", strvec_slot(strvec, 1)); return; } } current_vsyncg->smtp_alert = res; vrrp_data->num_smtp_alert++; } static void vrrp_gglobal_tracking_handler(__attribute__((unused)) const vector_t *strvec) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) global_tracking is deprecated. Use track_interface/script/file on the sync group", current_vsyncg->gname); current_vsyncg->sgroup_tracking_weight = true; } static void vrrp_sg_tracking_weight_handler(__attribute__((unused)) const vector_t *strvec) { current_vsyncg->sgroup_tracking_weight = true; } static void vrrp_sg_notify_priority_changes_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Invalid value '%s' for sync group notify_priority_changes specified", current_vsyncg->gname, strvec_slot(strvec, 1)); return; } } current_vsyncg->notify_priority_changes = res; } static void vrrp_handler(const vector_t *strvec) { vrrp_t *vrrp; const char *iname; global_data->have_vrrp_config = true; if (!strvec) return; if (vector_count(strvec) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_instance must have a name"); skip_block(true); return; } iname = strvec_slot(strvec,1); /* Make sure the vrrp instance doesn't already exist */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (!strcmp(iname, vrrp->iname)) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp instance %s already defined", iname); skip_block(true); return; } } current_vrrp = alloc_vrrp(iname); } static void vrrp_end_handler(void) { #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_FLAG_UNICAST_CONFIGURED, ¤t_vrrp->flags) && (__test_bit(VRRP_VMAC_BIT, ¤t_vrrp->flags) #ifdef _HAVE_VRRP_IPVLAN_ || (__test_bit(VRRP_IPVLAN_BIT, ¤t_vrrp->flags)) #endif )) { if (!current_vrrp->ifp) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): Cannot use VMAC/ipvlan with unicast and no interface - clearing use_vmac", current_vrrp->iname); __clear_bit(VRRP_VMAC_BIT, ¤t_vrrp->flags); #ifdef _HAVE_VRRP_IPVLAN_ __clear_bit(VRRP_IPVLAN_BIT, ¤t_vrrp->flags); #endif current_vrrp->vmac_ifname[0] = '\0'; } else if (!__test_bit(VRRP_VMAC_XMITBASE_BIT, ¤t_vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) unicast with use_vmac requires vmac_xmit_base - setting", current_vrrp->iname); __set_bit(VRRP_VMAC_XMITBASE_BIT, ¤t_vrrp->flags); } } #endif if (list_empty(¤t_vrrp->unicast_peer) && current_vrrp->ttl != -1) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): Cannot use unicast_ttl without unicast peers - resetting", current_vrrp->iname); current_vrrp->ttl = 0; } if (!current_vrrp->ifp) __clear_bit(VRRP_FLAG_LINKBEAT_USE_POLLING, ¤t_vrrp->flags); list_add_tail(¤t_vrrp->e_list, &vrrp_data->vrrp); } #ifdef _HAVE_VRRP_VMAC_ /* The following function is copied from kernel net/core/dev.c */ static bool __attribute__ ((pure)) dev_name_valid(const char *name) { if (*name == '\0') return false; if (strnlen(name, IFNAMSIZ) == IFNAMSIZ) return false; if (!strcmp(name, ".") || !strcmp(name, "..")) return false; while (*name) { if (*name == '/' || *name == ':' || isspace(*name)) return false; name++; } return true; } static void vrrp_vmac_handler(const vector_t *strvec) { interface_t *ifp; const char *name; vrrp_t *ovrrp; unsigned i; unsigned j; bool had_error; unsigned long byte_val; const char *p; char *endptr; __set_bit(VRRP_VMAC_BIT, ¤t_vrrp->flags); /* Ifname and MAC address can be specified */ for (i = 1; i < vector_size(strvec); i++) { if (strchr(strvec_slot(strvec, i), ':')) { /* It's a MAC address - interface names cannot include a ':' */ if (__test_bit(VRRP_VMAC_MAC_SPECIFIED, ¤t_vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "VMAC interface address already specified"); continue; } p = strvec_slot(strvec, i); if (p[strspn(p, "0123456789ABCDEFabcdef:")]) { report_config_error(CONFIG_GENERAL_ERROR, "VMAC invalid MAC address %s", p); continue; } for (j = 0, had_error = false; j < ETH_ALEN; j++) { errno = 0; byte_val = strtoul(p, &endptr, 16); if (errno || endptr - p > 2 || endptr == p) { had_error = true; break; } if (*endptr != ':' && (*endptr || j < ETH_ALEN - 2)) { had_error = true; break; } current_vrrp->ll_addr[j] = (u_char)byte_val; if (j == ETH_ALEN - 2 && (!*endptr || (*endptr == ':' && !*(endptr+1)))) { __set_bit(VRRP_VMAC_MAC_USE_VRID, ¤t_vrrp->flags); break; } p = endptr + 1; if (!*p && j < ETH_ALEN - 1) { /* Not enough octets specified */ had_error = true; break; } } if (had_error) report_config_error(CONFIG_GENERAL_ERROR, "VMAC invalid MAC address %s - ignored", strvec_slot(strvec, i)); else if (current_vrrp->ll_addr[0] & 0x01) report_config_error(CONFIG_GENERAL_ERROR, "VMAC MAC address is multicast %s - ignoring", strvec_slot(strvec, i)); else if (!memcmp(ll_addr, current_vrrp->ll_addr, ETH_ALEN - 2) && (current_vrrp->ll_addr[ETH_ALEN - 2] == 0x01 || current_vrrp->ll_addr[ETH_ALEN - 2] == 0x02)) report_config_error(CONFIG_GENERAL_ERROR, "VMAC MAC address not allowed to be RFC5798 address (%s) - ignoring", strvec_slot(strvec, i)); else __set_bit(VRRP_VMAC_MAC_SPECIFIED, ¤t_vrrp->flags); continue; } if (!strcmp(strvec_slot(strvec, i), "netlink_notify_msg")) { __set_bit(VRRP_VMAC_NETLINK_NOTIFY, ¤t_vrrp->flags); continue; } #if HAVE_DECL_FRA_SUPPRESS_IFGROUP if (!strcmp(strvec_slot(strvec, i), "group")) { uint32_t group; if (!find_rttables_group(strvec_slot(strvec, ++i), &group)) { report_config_error(CONFIG_GENERAL_ERROR, "VMAC group %s not found", strvec_slot(strvec, i)); continue; } __set_bit(VRRP_VMAC_GROUP, ¤t_vrrp->flags); current_vrrp->vmac_group = group; continue; } #endif if (!strcmp(strvec_slot(strvec, i), "name")) { /* Skip over "name" */ i++; } if (current_vrrp->vmac_ifname[0]) { report_config_error(CONFIG_GENERAL_ERROR, "VMAC interface name already specified"); continue; } name = strvec_slot(strvec, i); if (!dev_name_valid(name)) { report_config_error(CONFIG_GENERAL_ERROR, "VMAC interface name '%s' too long or invalid characters - ignoring", name); continue; } /* Check another vrrp instance isn't using this name */ list_for_each_entry(ovrrp, &vrrp_data->vrrp, e_list) { if (!strcmp(name, ovrrp->vmac_ifname)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRP instance %s is already using %s - ignoring name", current_vrrp->iname, ovrrp->iname, name); name = NULL; break; } } if (!name) continue; strcpy(current_vrrp->vmac_ifname, name); /* Check if the interface exists and is a macvlan we can use */ if ((ifp = if_get_by_ifname(current_vrrp->vmac_ifname, IF_NO_CREATE)) && (ifp->if_type != IF_TYPE_MACVLAN || ifp->vmac_type != MACVLAN_MODE_PRIVATE)) { /* ??? also check ADDR_GEN_MODE and VRF enslavement matches parent */ report_config_error(CONFIG_GENERAL_ERROR, "(%s) interface %s already exists and is not a private macvlan; ignoring vmac if_name", current_vrrp->iname, current_vrrp->vmac_ifname); current_vrrp->vmac_ifname[0] = '\0'; } } } static void vrrp_vmac_addr_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_VMAC_ADDR_BIT, ¤t_vrrp->flags); } static void vrrp_vmac_xmit_base_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_VMAC_XMITBASE_BIT, ¤t_vrrp->flags); } #endif #ifdef _HAVE_VRRP_IPVLAN_ static void vrrp_ipvlan_handler(const vector_t *strvec) { vrrp_t *ovrrp; interface_t *ifp; bool had_flags = false; ip_address_t addr = {0}; size_t i; const char *ifname; if (__test_bit(VRRP_IPVLAN_BIT, ¤t_vrrp->flags)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) use_ipvlan already specified", current_vrrp->iname); return; } __set_bit(VRRP_IPVLAN_BIT, ¤t_vrrp->flags); for (i = 1; i < vector_size(strvec); i++) { if (!strcmp(strvec_slot(strvec, i), "bridge")) { if (had_flags) report_config_error(CONFIG_GENERAL_ERROR, "(%s) ipvlan type already specified - ignoring '%s'", current_vrrp->iname, strvec_slot(strvec, i)); else { current_vrrp->ipvlan_type = 0; had_flags = true; } continue; } if (!strcmp(strvec_slot(strvec, i), "private")) { if (had_flags) report_config_error(CONFIG_GENERAL_ERROR, "(%s) ipvlan type already specified - ignoring '%s'", current_vrrp->iname, strvec_slot(strvec, i)); else { #ifdef IPVLAN_F_PRIVATE current_vrrp->ipvlan_type = IPVLAN_F_PRIVATE; #else report_config_error(CONFIG_GENERAL_ERROR, "(%s) kernel doesn't support ipvlan type %s", current_vrrp->iname, strvec_slot(strvec, i)); #endif had_flags = true; } continue; } if (!strcmp(strvec_slot(strvec, i), "vepa")) { if (had_flags) report_config_error(CONFIG_GENERAL_ERROR, "(%s) ipvlan type already specified - ignoring '%s'", current_vrrp->iname, strvec_slot(strvec, i)); else { #ifdef IPVLAN_F_VEPA current_vrrp->ipvlan_type = IPVLAN_F_VEPA; #else report_config_error(CONFIG_GENERAL_ERROR, "(%s) kernel doesn't support ipvlan type %s", current_vrrp->iname, strvec_slot(strvec, i)); #endif had_flags = true; } continue; } #if HAVE_DECL_FRA_SUPPRESS_IFGROUP if (!strcmp(strvec_slot(strvec, i), "group")) { uint32_t group; if (!find_rttables_group(strvec_slot(strvec, ++i), &group)) { report_config_error(CONFIG_GENERAL_ERROR, "ipvlan group %s not found", strvec_slot(strvec, i)); continue; } __set_bit(VRRP_VMAC_GROUP, ¤t_vrrp->flags); current_vrrp->vmac_group = group; continue; } #endif if (!strcmp(strvec_slot(strvec, i), "name")) { i++; } else if (check_valid_ipaddress(strvec_slot(strvec, i), true)) { parse_ipaddress(&addr, strvec_slot(strvec, i), true); if (current_vrrp->ipvlan_addr) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) ipvlan address already specified - ignoring '%s'", current_vrrp->iname, strvec_slot(strvec, i)); continue; } if (current_vrrp->family == AF_UNSPEC) current_vrrp->family = addr.ifa.ifa_family; else if (addr.ifa.ifa_family != current_vrrp->family) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) ipvlan address" "[%s] MUST match vrrp instance family !!! Skipping..." , current_vrrp->iname, strvec_slot(strvec, i)); continue; } current_vrrp->ipvlan_addr = MALLOC(sizeof(*current_vrrp->ipvlan_addr)); *current_vrrp->ipvlan_addr = addr; /* We also want to use this address as the source address */ if (current_vrrp->saddr.ss_family == AF_UNSPEC) { current_vrrp->saddr.ss_family = current_vrrp->ipvlan_addr->ifa.ifa_family; if (current_vrrp->saddr.ss_family == AF_INET) PTR_CAST(struct sockaddr_in, ¤t_vrrp->saddr)->sin_addr = current_vrrp->ipvlan_addr->u.sin.sin_addr; else PTR_CAST(struct sockaddr_in6, ¤t_vrrp->saddr)->sin6_addr = current_vrrp->ipvlan_addr->u.sin6_addr; __set_bit(VRRP_FLAG_SADDR_FROM_CONFIG, ¤t_vrrp->flags); } continue; } if (current_vrrp->vmac_ifname[0]) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) IPVLAN interface already specified - ignoring '%s'", current_vrrp->iname, strvec_slot(strvec, i)); continue; } ifname = strvec_slot(strvec, i); if (strlen(ifname) >= IFNAMSIZ) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) IPVLAN interface name '%s' too long - ignoring", current_vrrp->iname, ifname); continue; } /* Check another vrrp instance isn't using this name */ list_for_each_entry(ovrrp, &vrrp_data->vrrp, e_list) { if (!strcmp(ifname, ovrrp->vmac_ifname)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRRP instance %s is already using %s - ignoring name", current_vrrp->iname, ovrrp->iname, ifname); ifname = NULL; break; } } if (!ifname) continue; strcpy(current_vrrp->vmac_ifname, ifname); /* Check if the interface exists and is ipvlan we can use ??? also check ADDR_GEN_MODE and VRF enslavement matches parent */ if ((ifp = if_get_by_ifname(current_vrrp->vmac_ifname, IF_NO_CREATE)) && (ifp->if_type != IF_TYPE_IPVLAN || ifp->vmac_type != IPVLAN_MODE_L2)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) interface %s already exists and is not an l2 ipvlan; ignoring ipvlan if_name", current_vrrp->iname, current_vrrp->vmac_ifname); current_vrrp->vmac_ifname[0] = '\0'; } } } #endif static void vrrp_unicast_peer_handler(const vector_t *strvec) { __set_bit(VRRP_FLAG_UNICAST_CONFIGURED, ¤t_vrrp->flags); alloc_value_block(alloc_vrrp_unicast_peer, strvec); } static void vrrp_unicast_fault_no_peer(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_FLAG_UNICAST_CONFIGURED, ¤t_vrrp->flags); __set_bit(VRRP_FLAG_UNICAST_FAULT_NO_PEERS, ¤t_vrrp->flags); } static void vrrp_check_unicast_src_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_FLAG_UNICAST_CONFIGURED, ¤t_vrrp->flags); __set_bit(VRRP_FLAG_CHECK_UNICAST_SRC, ¤t_vrrp->flags); } #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ static void vrrp_unicast_chksum_handler(const vector_t *strvec) { __set_bit(VRRP_FLAG_UNICAST_CONFIGURED, ¤t_vrrp->flags); if (vector_size(strvec) >= 2) { if (!strcmp(strvec_slot(strvec, 1), "never")) current_vrrp->unicast_chksum_compat = CHKSUM_COMPATIBILITY_NEVER; else report_config_error(CONFIG_GENERAL_ERROR, "(%s) Unknown old_unicast_chksum mode %s - ignoring", current_vrrp->iname, strvec_slot(strvec, 1)); } else current_vrrp->unicast_chksum_compat = CHKSUM_COMPATIBILITY_CONFIG; } #endif static void vrrp_native_ipv6_handler(__attribute__((unused)) const vector_t *strvec) { if (current_vrrp->family == AF_INET) { report_config_error(CONFIG_GENERAL_ERROR,"(%s) Cannot specify native_ipv6 with IPv4 addresses", current_vrrp->iname); return; } current_vrrp->family = AF_INET6; current_vrrp->version = VRRP_VERSION_3; } static void vrrp_state_handler(const vector_t *strvec) { const char *str = strvec_slot(strvec, 1); if (!strcmp(str, "MASTER")) current_vrrp->wantstate = VRRP_STATE_MAST; else if (!strcmp(str, "BACKUP")) { if (current_vrrp->wantstate == VRRP_STATE_MAST) report_config_error(CONFIG_GENERAL_ERROR, "(%s) state previously set as MASTER - ignoring BACKUP", current_vrrp->iname); else current_vrrp->wantstate = VRRP_STATE_BACK; } else { report_config_error(CONFIG_GENERAL_ERROR,"(%s) unknown state '%s', defaulting to BACKUP", current_vrrp->iname, str); current_vrrp->wantstate = VRRP_STATE_BACK; } } static void vrrp_int_handler(const vector_t *strvec) { const char *name = strvec_slot(strvec, 1); if (strlen(name) >= IFNAMSIZ) { report_config_error(CONFIG_GENERAL_ERROR, "Interface name '%s' too long - ignoring", name); return; } current_vrrp->ifp = if_get_by_ifname(name, IF_CREATE_IF_DYNAMIC); if (!current_vrrp->ifp) report_config_error(CONFIG_GENERAL_ERROR, "WARNING - interface %s for vrrp_instance %s doesn't exist", name, current_vrrp->iname); else if (current_vrrp->ifp->hw_type == ARPHRD_LOOPBACK) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) cannot use a loopback interface (%s) for vrrp - ignoring", current_vrrp->iname, current_vrrp->ifp->ifname); current_vrrp->ifp = NULL; } #ifdef _HAVE_VRRP_VMAC_ current_vrrp->configured_ifp = current_vrrp->ifp; #endif } #ifdef _HAVE_VRF_ static void vrrp_vrf_handler(const vector_t *strvec) { const char *name = strvec_slot(strvec, 1); if (strlen(name) >= IFNAMSIZ) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRF interface name '%s' too long - ignoring", current_vrrp->iname, name); return; } if (current_vrrp->ifp) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Cannot specify VRF interface and interface - ignoring", current_vrrp->iname); return; } if (current_vrrp->vrf_ifp) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) VRF interface already specified as '%s' - ignoring", current_vrrp->iname, current_vrrp->vrf_ifp->ifname); return; } current_vrrp->vrf_ifp = if_get_by_ifname(name, IF_CREATE_IF_DYNAMIC); if (!current_vrrp->vrf_ifp) report_config_error(CONFIG_GENERAL_ERROR, "(%s) WARNING - VRF interface %s doesn't exist", current_vrrp->iname, name); else if (current_vrrp->vrf_ifp->hw_type == ARPHRD_LOOPBACK) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) cannot use a loopback interface (%s) for VRF - ignoring", current_vrrp->iname, name); current_vrrp->vrf_ifp = NULL; } } #endif #ifdef _WITH_LINKBEAT_ static void vrrp_linkbeat_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_FLAG_LINKBEAT_USE_POLLING, ¤t_vrrp->flags); report_config_error(CONFIG_GENERAL_ERROR, "(%s) 'linkbeat_use_polling' in vrrp instance deprecated - use linkbeat_interfaces block", current_vrrp->iname); } #endif static void v3_checksum_as_v2(__attribute__((unused)) const vector_t *strvec) { int res; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec, 1)); if (res >= 0) { if (res) __set_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, ¤t_vrrp->flags); else __clear_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, ¤t_vrrp->flags); } else report_config_error(CONFIG_GENERAL_ERROR, "(%s) invalid v3_checksum_as_v2 %s specified", current_vrrp->iname, strvec_slot(strvec, 1)); } else { /* Defaults to true if specified */ __set_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, ¤t_vrrp->flags); } } static void vrrp_track_if_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_track_if, strvec); } static void vrrp_track_scr_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_track_script, strvec); } static void vrrp_track_file_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_track_file, strvec); } #ifdef _WITH_TRACK_PROCESS_ static void vrrp_track_process_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_track_process, strvec); } #endif static void vrrp_dont_track_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, ¤t_vrrp->flags); } #ifdef _WITH_BFD_ static void vrrp_track_bfd_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_track_bfd, strvec); } #endif static void vrrp_srcip_handler(const vector_t *strvec) { sockaddr_t *saddr = ¤t_vrrp->saddr; if (inet_stosockaddr(strvec_slot(strvec, 1), NULL, saddr)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: VRRP instance[%s] malformed" " src address[%s]. Skipping..." , current_vrrp->iname, strvec_slot(strvec, 1)); return; } __set_bit(VRRP_FLAG_SADDR_FROM_CONFIG, ¤t_vrrp->flags); if (current_vrrp->family == AF_UNSPEC) current_vrrp->family = saddr->ss_family; else if (saddr->ss_family != current_vrrp->family) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: VRRP instance[%s] and src address" "[%s] MUST be of the same family !!! Skipping..." , current_vrrp->iname, strvec_slot(strvec, 1)); saddr->ss_family = AF_UNSPEC; __clear_bit(VRRP_FLAG_SADDR_FROM_CONFIG, ¤t_vrrp->flags); } } static void vrrp_unicast_srcip_handler(const vector_t *strvec) { __set_bit(VRRP_FLAG_UNICAST_CONFIGURED, ¤t_vrrp->flags); vrrp_srcip_handler(strvec); } static void vrrp_mcast_dstip_handler(const vector_t *strvec) { if (inet_stosockaddr(strvec_slot(strvec, 1), NULL, ¤t_vrrp->mcast_daddr)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) malformed" " mcast dest address %s. Skipping..." , current_vrrp->iname, strvec_slot(strvec, 1)); return; } if (current_vrrp->family == AF_UNSPEC) current_vrrp->family = current_vrrp->mcast_daddr.ss_family; else if (current_vrrp->mcast_daddr.ss_family != current_vrrp->family) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) mcast dest address" " %s MUST match VRRP instance family. Skipping..." , current_vrrp->iname, strvec_slot(strvec, 1)); current_vrrp->mcast_daddr.ss_family = AF_UNSPEC; } if ((current_vrrp->mcast_daddr.ss_family == AF_INET && !IN_MULTICAST(htonl(PTR_CAST(struct sockaddr_in, ¤t_vrrp->mcast_daddr)->sin_addr.s_addr))) || (current_vrrp->mcast_daddr.ss_family == AF_INET6 && !IN6_IS_ADDR_MC_LINKLOCAL(&PTR_CAST(struct sockaddr_in6, ¤t_vrrp->mcast_daddr)->sin6_addr))) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) mcast_dst_ip %s not%s multicast. Skipping..." , current_vrrp->iname, strvec_slot(strvec, 1), current_vrrp->mcast_daddr.ss_family == AF_INET6 ? " link-local" : ""); current_vrrp->mcast_daddr.ss_family = AF_UNSPEC; } } static void vrrp_track_srcip_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_FLAG_TRACK_SADDR, ¤t_vrrp->flags); } static void vrrp_vrid_handler(const vector_t *strvec) { unsigned vrid; if (!read_unsigned_strvec(strvec, 1, &vrid, 1, 255, false)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): VRID '%s' not valid - must be between 1 & 255", current_vrrp->iname, strvec_slot(strvec, 1)); return; } current_vrrp->vrid = (uint8_t)vrid; } static void vrrp_ttl_handler(const vector_t *strvec) { unsigned ttl; if (!read_unsigned_strvec(strvec, 1, &ttl, 0, 255, false)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): TTL '%s' not valid - must be between 0 & 255", current_vrrp->iname, strvec_slot(strvec, 1)); return; } current_vrrp->ttl = (uint8_t)ttl; } static void vrrp_prio_handler(const vector_t *strvec) { unsigned base_priority; if (!read_unsigned_strvec(strvec, 1, &base_priority, 1, VRRP_PRIO_OWNER, false)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Priority not valid! must be between 1 & %d. Using default %d", current_vrrp->iname, VRRP_PRIO_OWNER, VRRP_PRIO_DFL); current_vrrp->base_priority = VRRP_PRIO_DFL; } else current_vrrp->base_priority = (uint8_t)base_priority; } static void vrrp_adv_handler(const vector_t *strvec) { unsigned adver_int; bool res; res = read_decimal_unsigned_strvec(strvec, 1, &adver_int, TIMER_HZ / 100, 255 * TIMER_HZ, TIMER_HZ_DIGITS, true); /* Simple check - just positive */ if (!res || adver_int <= 0) report_config_error(CONFIG_GENERAL_ERROR, "(%s) Advert interval (%s) not valid! Must be > 0 - ignoring", current_vrrp->iname, strvec_slot(strvec, 1)); else current_vrrp->adver_int = adver_int; } static void vrrp_debug_handler(const vector_t *strvec) { unsigned debug_val; if (!read_unsigned_strvec(strvec, 1, &debug_val, 0, 4, true)) report_config_error(CONFIG_GENERAL_ERROR, "(%s) Debug value '%s' not valid; must be between 0-4", current_vrrp->iname, strvec_slot(strvec, 1)); else current_vrrp->debug = debug_val; } static void vrrp_skip_check_adv_addr_handler(const vector_t *strvec) { int res; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec, 1)); if (res >= 0) __set_bit(VRRP_FLAG_SKIP_CHECK_ADV_ADDR, ¤t_vrrp->flags); else report_config_error(CONFIG_GENERAL_ERROR, "(%s) invalid skip_check_adv_addr %s specified", current_vrrp->iname, strvec_slot(strvec, 1)); } else { /* Defaults to true */ __set_bit(VRRP_FLAG_SKIP_CHECK_ADV_ADDR, ¤t_vrrp->flags); } } static void vrrp_strict_mode_handler(const vector_t *strvec) { int res; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec, 1)); if (res >= 0) current_vrrp->strict_mode = (bool)res; else report_config_error(CONFIG_GENERAL_ERROR, "(%s) invalid strict_mode %s specified", current_vrrp->iname, strvec_slot(strvec, 1)); } else { /* Defaults to true */ current_vrrp->strict_mode = true; } } static void vrrp_nopreempt_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_FLAG_NOPREEMPT, ¤t_vrrp->flags); } static void /* backwards compatibility */ vrrp_preempt_handler(__attribute__((unused)) const vector_t *strvec) { __clear_bit(VRRP_FLAG_NOPREEMPT, ¤t_vrrp->flags); } static void vrrp_preempt_delay_handler(const vector_t *strvec) { unsigned preempt_delay; if (!read_decimal_unsigned_strvec(strvec, 1, &preempt_delay, 0, TIMER_MAX_SEC * TIMER_HZ, TIMER_HZ_DIGITS, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Preempt_delay not valid! must be between 0-%u", current_vrrp->iname, TIMER_MAX_SEC); current_vrrp->preempt_delay = 0; } else current_vrrp->preempt_delay = preempt_delay; } static void vrrp_notify_backup_handler(const vector_t *strvec) { if (current_vrrp->script_backup) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_backup script already specified - ignoring %s", current_vrrp->iname, strvec_slot(strvec,1)); return; } current_vrrp->script_backup = set_vrrp_notify_script(strvec, 0); current_vrrp->notify_exec = true; } static void vrrp_notify_master_handler(const vector_t *strvec) { if (current_vrrp->script_master) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_master script already specified - ignoring %s", current_vrrp->iname, strvec_slot(strvec,1)); return; } current_vrrp->script_master = set_vrrp_notify_script(strvec, 0); current_vrrp->notify_exec = true; } static void vrrp_notify_fault_handler(const vector_t *strvec) { if (current_vrrp->script_fault) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_fault script already specified - ignoring %s", current_vrrp->iname, strvec_slot(strvec,1)); return; } current_vrrp->script_fault = set_vrrp_notify_script(strvec, 0); current_vrrp->notify_exec = true; } static void vrrp_notify_stop_handler(const vector_t *strvec) { if (current_vrrp->script_stop) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_stop script already specified - ignoring %s", current_vrrp->iname, strvec_slot(strvec,1)); return; } current_vrrp->script_stop = set_vrrp_notify_script(strvec, 0); current_vrrp->notify_exec = true; } static void vrrp_notify_deleted_handler(const vector_t *strvec) { if (current_vrrp->notify_deleted) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_deleted already specified - ignoring %s", current_vrrp->iname, vector_size(strvec) > 1 ? strvec_slot(strvec,1) : ""); return; } if (vector_size(strvec) > 1) { current_vrrp->script_deleted = set_vrrp_notify_script(strvec, 0); current_vrrp->notify_exec = true; } current_vrrp->notify_deleted = true; } static void vrrp_notify_handler(const vector_t *strvec) { if (current_vrrp->script) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify script already specified - ignoring %s", current_vrrp->iname, strvec_slot(strvec,1)); return; } current_vrrp->script = set_vrrp_notify_script(strvec, 4); current_vrrp->notify_exec = true; } static void vrrp_notify_master_rx_lower_pri(const vector_t *strvec) { if (current_vrrp->script_master_rx_lower_pri) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_master_rx_lower_pri script already specified - ignoring %s", current_vrrp->iname, strvec_slot(strvec,1)); return; } current_vrrp->script_master_rx_lower_pri = set_vrrp_notify_script(strvec, 0); current_vrrp->notify_exec = true; } static void vrrp_smtp_handler(__attribute__((unused)) const vector_t *strvec) { 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 vrrp_instance smtp_alert parameter %s", strvec_slot(strvec, 1)); return; } } current_vrrp->smtp_alert = res; vrrp_data->num_smtp_alert++; } static void vrrp_notify_priority_changes_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Invalid value '%s' for notify_priority_changes specified", current_vrrp->iname, strvec_slot(strvec, 1)); return; } } current_vrrp->notify_priority_changes = res; } #ifdef _WITH_LVS_ static void vrrp_lvs_syncd_handler(const vector_t *strvec) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) Specifying lvs_sync_daemon_interface against a vrrp is deprecated.", current_vrrp->iname); /* Deprecated after v1.2.19 */ report_config_error(CONFIG_GENERAL_ERROR, " %*sPlease use global lvs_sync_daemon", (int)strlen(current_vrrp->iname) - 2, ""); if (global_data->lvs_syncd.ifname) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) lvs_sync_daemon_interface has already been specified as %s - ignoring", current_vrrp->iname, global_data->lvs_syncd.ifname); return; } global_data->lvs_syncd.ifname = set_value(strvec); global_data->lvs_syncd.vrrp = current_vrrp; } #endif static void vrrp_garp_delay_handler(const vector_t *strvec) { unsigned delay; if (!read_unsigned_strvec(strvec, 1, &delay, 0, UINT_MAX / TIMER_HZ, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): garp_master_delay '%s' invalid - ignoring", current_vrrp->iname, strvec_slot(strvec, 1)); return; } current_vrrp->garp_delay = delay * TIMER_HZ; } static void vrrp_garp_refresh_handler(const vector_t *strvec) { unsigned refresh; if (!read_unsigned_strvec(strvec, 1, &refresh, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): Invalid garp_master_refresh '%s' - ignoring", current_vrrp->iname, strvec_slot(strvec, 1)); current_vrrp->garp_refresh.tv_sec = 0; } else current_vrrp->garp_refresh.tv_sec = refresh; current_vrrp->garp_refresh.tv_usec = 0; } static void vrrp_garp_rep_handler(const vector_t *strvec) { unsigned repeats; /* The min value should be 1, but allow 0 to maintain backward compatibility * with pre v2.0.7 */ if (!read_unsigned_strvec(strvec, 1, &repeats, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): garp_master_repeat '%s' invalid - ignoring", current_vrrp->iname, strvec_slot(strvec, 1)); return; } if (repeats == 0) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): garp_master_repeat must be greater than 0, setting to 1", current_vrrp->iname); repeats = 1; } current_vrrp->garp_rep = repeats; } static void vrrp_garp_refresh_rep_handler(const vector_t *strvec) { unsigned repeats; /* The min value should be 1, but allow 0 to maintain backward compatibility * with pre v2.0.7 */ if (!read_unsigned_strvec(strvec, 1, &repeats, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): garp_master_refresh_repeat '%s' invalid - ignoring", current_vrrp->iname, strvec_slot(strvec, 1)); return; } if (repeats == 0) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): garp_master_refresh_repeat must be greater than 0, setting to 1", current_vrrp->iname); repeats = 1; } current_vrrp->garp_refresh_rep = repeats; } static void vrrp_garp_lower_prio_delay_handler(const vector_t *strvec) { unsigned delay; if (!read_unsigned_strvec(strvec, 1, &delay, 0, UINT_MAX / TIMER_HZ, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): garp_lower_prio_delay '%s' invalid - ignoring", current_vrrp->iname, strvec_slot(strvec, 1)); return; } current_vrrp->garp_lower_prio_delay = delay * TIMER_HZ; } static void vrrp_garp_lower_prio_rep_handler(const vector_t *strvec) { unsigned garp_lower_prio_rep; if (!read_unsigned_strvec(strvec, 1, &garp_lower_prio_rep, 0, INT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): Invalid garp_lower_prio_repeat '%s'", current_vrrp->iname, strvec_slot(strvec, 1)); return; } current_vrrp->garp_lower_prio_rep = garp_lower_prio_rep; } static void vrrp_down_timer_adverts_handler(const vector_t *strvec) { unsigned down_timer_adverts; if (!read_unsigned_strvec(strvec, 1, &down_timer_adverts, 1, 100, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): Invalid down_timer_adverts [1:100] '%s'", current_vrrp->iname, strvec_slot(strvec, 1)); return; } current_vrrp->down_timer_adverts = down_timer_adverts; } static void vrrp_timer_expired_backup_handler(const vector_t *strvec) { unsigned other_priority = VRRP_PRIO_OWNER - 1; /* default to shortest duration for another instance to take over */ if (vector_size(strvec) >= 2) { if (!read_unsigned_strvec(strvec, 1, &other_priority, 1, VRRP_PRIO_OWNER - 1, false)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) timer_expired_backup highest_other_priority not valid! must be between 1 & %d", current_vrrp->iname, VRRP_PRIO_OWNER - 1); return; } } current_vrrp->highest_other_priority = (uint8_t)other_priority; } static void vrrp_thread_timer_expired_handler(const vector_t *strvec) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) thread_timer_expired - please replace with keyword 'timer_expired_backup'", current_vrrp->iname); vrrp_timer_expired_backup_handler(strvec); } #ifdef _HAVE_VRRP_VMAC_ static void vrrp_garp_extra_if_handler(const vector_t *strvec) { unsigned delay = UINT_MAX; unsigned index; const char *cmd_name = strvec_slot(strvec, 0); if (!strcmp(cmd_name, "vmac_garp_intvl")) { /* Deprecated after v2.2.2 */ report_config_error(CONFIG_DEPRECATED, "Keyword \"vmac_garp_intvl\" is deprecated - please use \"garp_extra_if\""); } for (index = 1; index < vector_size(strvec); index++) { if (!strcmp(strvec_slot(strvec, index), "all")) __set_bit(VRRP_FLAG_VMAC_GARP_ALL_IF, ¤t_vrrp->flags); else if (!read_unsigned_strvec(strvec, index, &delay, 0, 86400, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): %s '%s' invalid - ignoring", current_vrrp->iname, cmd_name, strvec_slot(strvec, index)); return; } } if (delay == UINT_MAX) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): %s specified without time - ignoring", current_vrrp->iname, cmd_name); return; } current_vrrp->vmac_garp_intvl.tv_sec = delay; current_vrrp->vmac_garp_intvl.tv_usec = 0; } #endif static void vrrp_lower_prio_no_advert_handler(const vector_t *strvec) { int res; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec, 1)); if (res >= 0) current_vrrp->lower_prio_no_advert = (unsigned)res; else report_config_error(CONFIG_GENERAL_ERROR, "(%s) invalid lower_prio_no_advert %s specified", current_vrrp->iname, strvec_slot(strvec, 1)); } else { /* Defaults to true */ current_vrrp->lower_prio_no_advert = true; } } static void vrrp_higher_prio_send_advert_handler(const vector_t *strvec) { int res; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec, 1)); if (res >= 0) current_vrrp->higher_prio_send_advert = (unsigned)res; else report_config_error(CONFIG_GENERAL_ERROR, "(%s) invalid higher_prio_send_advert %s specified", current_vrrp->iname, strvec_slot(strvec, 1)); } else { /* Defaults to true */ current_vrrp->higher_prio_send_advert = true; } } static void vrrp_owner_ignore_adverts_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec, 1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) invalid %s %s specified", current_vrrp->iname, strvec_slot(strvec, 0), strvec_slot(strvec, 1)); return; } } current_vrrp->owner_ignore_adverts = (unsigned)res; } static void kernel_rx_buf_size_handler(const vector_t *strvec) { unsigned rx_buf_size; if (vector_size(strvec) == 2 && read_unsigned_strvec(strvec, 1, &rx_buf_size, 0, UINT_MAX, false)) { current_vrrp->kernel_rx_buf_size = rx_buf_size; return; } report_config_error(CONFIG_GENERAL_ERROR, "(%s) invalid kernel_rx_buf_size specified", current_vrrp->iname); } #if defined _WITH_VRRP_AUTH_ static void vrrp_auth_type_handler(const vector_t *strvec) { const char *str = strvec_slot(strvec, 1); if (!strcmp(str, "AH")) current_vrrp->auth_type = VRRP_AUTH_AH; else if (!strcmp(str, "PASS")) current_vrrp->auth_type = VRRP_AUTH_PASS; else report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown authentication type '%s'", current_vrrp->iname, str); } static void vrrp_auth_pass_handler(const vector_t *strvec) { const char *str = strvec_slot(strvec, 1); size_t max_size = sizeof (current_vrrp->auth_data); size_t str_len = strlen(str); if (str_len > max_size) { str_len = max_size; report_config_error(CONFIG_GENERAL_ERROR, "Truncating auth_pass to %zu characters", max_size); } memset(current_vrrp->auth_data, 0, max_size); memcpy(current_vrrp->auth_data, str, str_len); } #endif static void vrrp_vip_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_vip, strvec); } static void vrrp_evip_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_evip, strvec); } static void vrrp_no_vip_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_FLAG_ALLOW_NO_VIPS, ¤t_vrrp->flags); } static void vrrp_promote_secondaries_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(VRRP_FLAG_PROMOTE_SECONDARIES, ¤t_vrrp->flags); } static void vrrp_vroutes_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_vroute, strvec); } static void vrrp_vrules_handler(const vector_t *strvec) { alloc_value_block(alloc_vrrp_vrule, strvec); } static void vrrp_script_handler(const vector_t *strvec) { if (!strvec) return; current_vscr = alloc_vrrp_script(strvec_slot(strvec, 1)); script_user_set = false; remove_script = false; } static void vrrp_vscript_script_handler(__attribute__((unused)) const vector_t *strvec) { const vector_t *strvec_qe; /* We need to allow quoted and escaped strings for the script and parameters */ strvec_qe = alloc_strvec_quoted(NULL); set_script_params_array(strvec_qe, ¤t_vscr->script, 0); free_strvec(strvec_qe); } static void vrrp_vscript_interval_handler(const vector_t *strvec) { unsigned interval; /* The min value should be 0.001, but allow 0 to maintain backward compatibility * with pre v2.0.7 */ if (!read_decimal_unsigned_strvec(strvec, 1, &interval, 0, (UINT_MAX / TIMER_HZ) * 1000, 3, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): vrrp script interval '%s' must be between 0.001 and %u - ignoring", current_vscr->sname, strvec_slot(strvec, 1), UINT_MAX / TIMER_HZ); return; } if (interval == 0) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): vrrp script interval must be greater than 0, setting to 1", current_vscr->sname); interval = 1000; } current_vscr->interval = interval * (TIMER_HZ / 1000); } static void vrrp_vscript_timeout_handler(const vector_t *strvec) { unsigned timeout; /* The min value should be 1, but allow 0 to maintain backward compatibility * with pre v2.0.7 */ if (!read_decimal_unsigned_strvec(strvec, 1, &timeout, 0, (UINT_MAX / TIMER_HZ) * 1000, 3, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): vrrp script timeout '%s' invalid - ignoring", current_vscr->sname, strvec_slot(strvec, 1)); return; } if (timeout == 0) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): vrrp script timeout must be greater than 0, setting to 1", current_vscr->sname); timeout = 1000; } current_vscr->timeout = timeout * (TIMER_HZ / 1000); } static void vrrp_vscript_weight_handler(const vector_t *strvec) { int weight; if (!read_int_strvec(strvec, 1, &weight, -253, 253, true)) report_config_error(CONFIG_GENERAL_ERROR, "vrrp_script %s weight %s must be in [-253, 253]", current_vscr->sname, strvec_slot(strvec, 1)); current_vscr->weight = weight; if (vector_size(strvec) >= 3) { if (!strcmp(strvec_slot(strvec, 2), "reverse")) current_vscr->weight_reverse = true; else report_config_error(CONFIG_GENERAL_ERROR, "vrrp_script %s unknown weight option %s", current_vscr->sname, strvec_slot(strvec, 2)); } } static void vrrp_vscript_rise_handler(const vector_t *strvec) { unsigned rise; if (!read_unsigned_strvec(strvec, 1, &rise, 1, INT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): vrrp script rise value '%s' invalid, defaulting to 1", current_vscr->sname, strvec_slot(strvec, 1)); current_vscr->rise = 1; } else current_vscr->rise = rise; } static void vrrp_vscript_fall_handler(const vector_t *strvec) { unsigned fall; if (!read_unsigned_strvec(strvec, 1, &fall, 1, INT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): vrrp script fall value '%s' invalid, defaulting to 1", current_vscr->sname, strvec_slot(strvec, 1)); current_vscr->fall = 1; } else current_vscr->fall = fall; } static void vrrp_vscript_user_handler(const vector_t *strvec) { if (set_script_uid_gid(strvec, 1, ¤t_vscr->script.uid, ¤t_vscr->script.gid)) { report_config_error(CONFIG_GENERAL_ERROR, "Unable to set uid/gid for script %s" , cmd_str(¤t_vscr->script)); remove_script = true; } else { remove_script = false; script_user_set = true; } } static void vrrp_vscript_end_handler(void) { if (!current_vscr->script.args || !current_vscr->script.args[0]) { report_config_error(CONFIG_GENERAL_ERROR, "No script set for vrrp_script %s - removing" , current_vscr->sname); remove_script = true; } else if (!remove_script && !script_user_set) { if (get_default_script_user(¤t_vscr->script.uid, ¤t_vscr->script.gid)) { report_config_error(CONFIG_GENERAL_ERROR, "Unable to set default user for vrrp" " script %s - removing" , current_vscr->sname); remove_script = true; } } if (remove_script) { free_vscript(current_vscr); return; } list_add_tail(¤t_vscr->e_list, &vrrp_data->vrrp_script); } #ifdef _WITH_TRACK_PROCESS_ static void vrrp_tprocess_handler(const vector_t *strvec) { if (!strvec) return; if (proc_events_not_supported) report_config_error(CONFIG_GENERAL_ERROR, "no kernel support for track_process (CONFIG_PROC_EVENTS)"); current_tp = alloc_vrrp_process(strvec_slot(strvec, 1)); } static void vrrp_tprocess_process_handler(const vector_t *strvec) { size_t len = 0; size_t i; char *p; if (current_tp->process_path) { report_config_error(CONFIG_GENERAL_ERROR, "Process already set for track process %s" " - ignoring %s" , current_tp->pname, strvec_slot(strvec, 1)); return; } current_tp->process_path = set_value(strvec); if (vector_size(strvec) > 2) { for (i = 2; i < vector_size(strvec); i++) len += strlen(strvec_slot(strvec, i)) + 1; current_tp->process_params = p = MALLOC(len); current_tp->process_params_len = len; for (i = 2; i < vector_size(strvec); i++) { strcpy(p, strvec_slot(strvec, i)); p += strlen(strvec_slot(strvec, i)) + 1; } if (current_tp->param_match == PARAM_MATCH_NONE) current_tp->param_match = PARAM_MATCH_EXACT; current_tp->full_command = true; } len += strlen(current_tp->process_path) + 1; if (len > vrrp_data->vrrp_max_process_name_len) vrrp_data->vrrp_max_process_name_len = len; } static void vrrp_tprocess_match_handler(const vector_t *strvec) { if (vector_size(strvec) == 1) { current_tp->param_match = PARAM_MATCH_EXACT; current_tp->full_command = true; } else if (!strcmp(strvec_slot(strvec, 1), "initial")) { current_tp->param_match = PARAM_MATCH_INITIAL; current_tp->full_command = true; } else if (!strcmp(strvec_slot(strvec, 1), "partial")) { current_tp->param_match = PARAM_MATCH_PARTIAL; current_tp->full_command = true; } else report_config_error(CONFIG_GENERAL_ERROR, "Invalid param_match type %s - ignoring", strvec_slot(strvec, 1)); } static void vrrp_tprocess_weight_handler(const vector_t *strvec) { int weight; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No weight specified for track process %s - ignoring", current_tp->pname); return; } if (current_tp->weight) { report_config_error(CONFIG_GENERAL_ERROR, "Weight already set for track process %s - ignoring %s", current_tp->pname, strvec_slot(strvec, 1)); return; } if (!read_int_strvec(strvec, 1, &weight, -254, 254, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Weight (%s) for vrrp_track_process %s must be between " "[-254..254] inclusive. Ignoring...", strvec_slot(strvec, 1), current_tp->pname); return; } if (vector_size(strvec) >= 3) { if (!strcmp(strvec_slot(strvec, 2), "reverse")) current_tp->weight_reverse = true; else report_config_error(CONFIG_GENERAL_ERROR, "vrrp_track_process %s unknown weight option %s", current_tp->pname, strvec_slot(strvec, 2)); } current_tp->weight = weight; } static void vrrp_tprocess_quorum_handler(const vector_t *strvec) { unsigned quorum; if (!read_unsigned_strvec(strvec, 1, &quorum, 1, 65535, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Quorum (%s) for vrrp_track_process %s must be between " "[1..65535] inclusive. Ignoring...", strvec_slot(strvec, 1), current_tp->pname); return; } if (quorum > current_tp->quorum_max) { report_config_error(CONFIG_GENERAL_ERROR, "Quorum is greater than quorum_max - ignoring"); return; } current_tp->quorum = quorum; } static void vrrp_tprocess_quorum_max_handler(const vector_t *strvec) { unsigned quorum_max; if (!read_unsigned_strvec(strvec, 1, &quorum_max, 0, 65535, true)) { report_config_error(CONFIG_GENERAL_ERROR, "quorum_max (%s) for vrrp_track_process %s must be between " "[0..65535] inclusive. Ignoring...", strvec_slot(strvec, 1), current_tp->pname); return; } /* Allow quorum_max = 0 if quorum not specified */ if (quorum_max || current_tp->quorum > 1) { if (quorum_max < current_tp->quorum) { report_config_error(CONFIG_GENERAL_ERROR, "quorum_max is less than quorum - ignoring"); return; } } current_tp->quorum_max = quorum_max; if (quorum_max == 0) current_tp->quorum = 0; else if (!current_tp->quorum) current_tp->quorum = 1; } static void vrrp_tprocess_delay_general(const vector_t *strvec, enum process_delay delay_type) { unsigned delay; if (!read_decimal_unsigned_strvec(strvec, 1, &delay, 1, 3600U * TIMER_HZ, TIMER_HZ_DIGITS, true)) { report_config_error(CONFIG_GENERAL_ERROR, "%sdelay (%s) for vrrp_track_process %s must be between " "[0.000001..3600] inclusive. Ignoring..." , delay_type == PROCESS_TERMINATE_DELAY ? "terminate_" : delay_type == PROCESS_FORK_DELAY ? "fork_" : "" , strvec_slot(strvec, 1), current_tp->pname); return; } if (delay_type != PROCESS_FORK_DELAY) current_tp->terminate_delay = delay; if (delay_type != PROCESS_TERMINATE_DELAY) current_tp->fork_delay = delay; } static void vrrp_tprocess_terminate_delay_handler(const vector_t *strvec) { vrrp_tprocess_delay_general(strvec, PROCESS_TERMINATE_DELAY); } static void vrrp_tprocess_fork_delay_handler(const vector_t *strvec) { vrrp_tprocess_delay_general(strvec, PROCESS_FORK_DELAY); } static void vrrp_tprocess_delay_handler(const vector_t *strvec) { vrrp_tprocess_delay_general(strvec, PROCESS_DELAY); } static void vrrp_tprocess_full_handler(__attribute__((unused)) const vector_t *strvec) { current_tp->full_command = true; } static void vrrp_tprocess_end_handler(void) { if (proc_events_not_supported) { free_vprocess(current_tp); return; } if (!current_tp->process_path) { report_config_error(CONFIG_GENERAL_ERROR, "track process '%s' process name not specified" , current_tp->pname); free_vprocess(current_tp); return; } if (current_tp->full_command) vrrp_data->vrrp_use_process_cmdline = true; else vrrp_data->vrrp_use_process_comm = true; list_add_tail(¤t_tp->e_list, &vrrp_data->vrrp_track_processes); } #endif static void vrrp_vscript_init_fail_handler(__attribute__((unused)) const vector_t *strvec) { current_vscr->init_state = SCRIPT_INIT_STATE_FAILED; } static void vrrp_version_handler(const vector_t *strvec) { int version; if (!read_int_strvec(strvec, 1, &version, 2, 3, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): Version must be either 2 or 3" , current_vrrp->iname); return; } if ((current_vrrp->version && current_vrrp->version != version) || (version == VRRP_VERSION_2 && current_vrrp->family == AF_INET6)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) vrrp_version %d conflicts with configured" " or deduced version %d; ignoring." , current_vrrp->iname, version, current_vrrp->version); return; } current_vrrp->version = version; } static void vrrp_accept_handler(__attribute__((unused)) const vector_t *strvec) { #ifdef _WITH_FIREWALL_ current_vrrp->accept = true; #endif } #ifdef _WITH_FIREWALL_ static void vrrp_no_accept_handler(__attribute__((unused)) const vector_t *strvec) { current_vrrp->accept = false; } #endif static void garp_group_handler(const vector_t *strvec) { if (!strvec) return; current_ggd = alloc_garp_delay(); } static void garp_group_garp_interval_handler(const vector_t *strvec) { unsigned val; if (!read_decimal_unsigned_strvec(strvec, 1, &val, 0, UINT_MAX, TIMER_HZ_DIGITS, true)) { report_config_error(CONFIG_GENERAL_ERROR, "garp_group garp_interval '%s' invalid", strvec_slot(strvec, 1)); return; } current_ggd->garp_interval.tv_sec = (time_t)val / TIMER_HZ; current_ggd->garp_interval.tv_usec = (suseconds_t)(val % TIMER_HZ); current_ggd->have_garp_interval = true; if (current_ggd->garp_interval.tv_sec >= 1) log_message(LOG_INFO, "The garp_interval is very large - %s seconds", strvec_slot(strvec,1)); } static void garp_group_gna_interval_handler(const vector_t *strvec) { unsigned val; if (!read_decimal_unsigned_strvec(strvec, 1, &val, 0, UINT_MAX, TIMER_HZ_DIGITS, true)) { report_config_error(CONFIG_GENERAL_ERROR, "garp_group gna_interval '%s' invalid", strvec_slot(strvec, 1)); return; } current_ggd->gna_interval.tv_sec = (time_t)val / TIMER_HZ; current_ggd->gna_interval.tv_usec = (suseconds_t)(val % TIMER_HZ); current_ggd->have_gna_interval = true; if (current_ggd->gna_interval.tv_sec >= 1) log_message(LOG_INFO, "The gna_interval is very large - %s seconds", strvec_slot(strvec,1)); } static void garp_group_interface_handler(const vector_t *strvec) { interface_t *ifp = if_get_by_ifname(strvec_slot(strvec, 1), IF_CREATE_IF_DYNAMIC); if (!ifp) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - interface %s specified for garp_group doesn't exist", strvec_slot(strvec, 1)); return; } if (ifp->garp_delay) { report_config_error(CONFIG_GENERAL_ERROR, "garp_group already specified for %s - ignoring", strvec_slot(strvec, 1)); return; } #ifdef _HAVE_VRRP_VMAC_ /* We cannot have a group on a vmac interface */ if (IS_MAC_IP_VLAN(ifp)) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify garp_delay on a vmac (%s) - ignoring", ifp->ifname); return; } #endif ifp->garp_delay = current_ggd; } static void garp_group_interfaces_handler(const vector_t *strvec) { interface_t *ifp; const vector_t *interface_vec = read_value_block(strvec); size_t i; /* Handle the interfaces block being empty */ if (!interface_vec) { report_config_error(CONFIG_GENERAL_ERROR, "Warning - empty garp_group interfaces block"); return; } /* First set the configuration aggregation group number */ cur_aggregation_group++; for (i = 0; i < vector_size(interface_vec); i++) { ifp = if_get_by_ifname(vector_slot(interface_vec, i), IF_CREATE_IF_DYNAMIC); if (!ifp) { if (global_data->dynamic_interfaces) log_message(LOG_INFO, "WARNING - interface %s specified for garp_group doesn't exist", strvec_slot(interface_vec, i)); else report_config_error(CONFIG_GENERAL_ERROR, "WARNING - interface %s specified for garp_group doesn't exist", strvec_slot(interface_vec, i)); continue; } if (ifp->garp_delay) { report_config_error(CONFIG_GENERAL_ERROR, "garp_group already specified for %s - ignoring", strvec_slot(interface_vec, 1)); continue; } #ifdef _HAVE_VRRP_VMAC_ if (IS_MAC_IP_VLAN(ifp)) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify garp_delay on a vmac (%s) - ignoring", ifp->ifname); continue; } #endif ifp->garp_delay = current_ggd; } free_strvec(interface_vec); } static void garp_group_end_handler(void) { interface_t *ifp; list_head_t *ifq; if (!current_ggd->have_garp_interval && !current_ggd->have_gna_interval) { report_config_error(CONFIG_GENERAL_ERROR, "garp group %u does not have any delay set - removing", cur_aggregation_group); /* Remove the garp_delay from any interfaces that are using it */ ifq = get_interface_queue(); list_for_each_entry(ifp, ifq, e_list) { if (ifp->garp_delay == current_ggd) ifp->garp_delay = NULL; } free_garp_delay(current_ggd); } } static void alloc_if_up_down_delay(const vector_t *strvec) { unsigned down_delay = 0; unsigned up_delay = 0; int res; unsigned long delay; interface_t *ifp; if (!(ifp = if_get_by_ifname(strvec_slot(strvec, 0), global_data->dynamic_interfaces))) { report_config_error(CONFIG_FATAL, "unknown interface %s specified for up/down delay", strvec_slot(strvec, 0)); return; } if (vector_size(strvec) < 2) { log_message(LOG_INFO, "No timeouts specified for %s up/down delays", ifp->ifname); return; } if (vector_size(strvec) > 3) log_message(LOG_INFO, "Too many parameters for %s up/down delays", ifp->ifname); res = read_timer(strvec, 1, &delay, 0, 255 * TIMER_HZ, true); if (!res) { log_message(LOG_INFO, "Invalid down delay %s for %s", strvec_slot(strvec, 1), ifp->ifname); return; } down_delay = (unsigned)delay; if (vector_size(strvec) == 2) up_delay = 0; else { res = read_timer(strvec, 2, &delay, 0, 255 * TIMER_HZ, true); if (!res) { log_message(LOG_INFO, "Invalid up delay %s for %s", strvec_slot(strvec, 2), ifp->ifname); return; } up_delay = (unsigned)delay; } ifp->down_debounce_timer = down_delay; ifp->up_debounce_timer = up_delay; } /* interface state change delays handler */ static void interface_up_down_delays_handler(const vector_t *strvec) { if (!strvec) return; alloc_value_block(alloc_if_up_down_delay, strvec); } void init_vrrp_keywords(bool active) { #if defined _WITH_VRRP_AUTH_ vpp_t check_ptr; #endif /* Track group declarations */ install_keyword_root("track_group", &static_track_group_handler, active, VPP ¤t_stg); install_keyword("group", &static_track_group_group_handler); install_level_end_handler(&static_track_group_end_handler); /* Static addresses/routes/rules declarations */ install_keyword_root("static_ipaddress", &static_addresses_handler, active, NULL); install_keyword_root("static_routes", &static_routes_handler, active, NULL); install_keyword_root("static_rules", &static_rules_handler, active, NULL); /* Sync group declarations */ install_keyword_root("vrrp_sync_group", &vrrp_sync_group_handler, active, VPP ¤t_vsyncg); install_keyword("group", &vrrp_group_handler); install_keyword("track_interface", &vrrp_group_track_if_handler); install_keyword("track_script", &vrrp_group_track_scr_handler); install_keyword("track_file", &vrrp_group_track_file_handler); #ifdef _WITH_TRACK_PROCESS_ install_keyword("track_process", &vrrp_group_track_process_handler); #endif #ifdef _WITH_BFD_ install_keyword("track_bfd", &vrrp_group_track_bfd_handler); #endif install_keyword_quoted("notify_backup", &vrrp_gnotify_backup_handler); install_keyword_quoted("notify_master", &vrrp_gnotify_master_handler); install_keyword_quoted("notify_fault", &vrrp_gnotify_fault_handler); install_keyword_quoted("notify_stop", &vrrp_gnotify_stop_handler); install_keyword_quoted("notify", &vrrp_gnotify_handler); install_keyword("smtp_alert", &vrrp_gsmtp_handler); install_keyword("global_tracking", &vrrp_gglobal_tracking_handler); install_keyword("sync_group_tracking_weight", &vrrp_sg_tracking_weight_handler); install_keyword("notify_priority_changes", &vrrp_sg_notify_priority_changes_handler); install_level_end_handler(&vrrp_sync_group_end_handler); /* Garp declarations */ install_keyword_root("garp_group", &garp_group_handler, active, VPP ¤t_ggd); install_keyword("garp_interval", &garp_group_garp_interval_handler); install_keyword("gna_interval", &garp_group_gna_interval_handler); install_keyword("interface", &garp_group_interface_handler); install_keyword("interfaces", &garp_group_interfaces_handler); install_level_end_handler(&garp_group_end_handler); #ifdef _WITH_LINKBEAT_ /* Linkbeat interfaces */ install_keyword_root("linkbeat_interfaces", &linkbeat_interfaces_handler, active, NULL); #endif /* VRRP Instance declarations */ install_keyword_root("vrrp_instance", &vrrp_handler, active, VPP ¤t_vrrp); install_level_end_handler(&vrrp_end_handler); #ifdef _HAVE_VRRP_VMAC_ install_keyword("use_vmac", &vrrp_vmac_handler); install_keyword("use_vmac_addr", &vrrp_vmac_addr_handler); install_keyword("vmac_xmit_base", &vrrp_vmac_xmit_base_handler); #endif #ifdef _HAVE_VRRP_IPVLAN_ install_keyword("use_ipvlan", &vrrp_ipvlan_handler); #endif install_keyword("unicast_peer", &vrrp_unicast_peer_handler); install_keyword("unicast_fault_no_peer", &vrrp_unicast_fault_no_peer); install_keyword("check_unicast_src", &vrrp_check_unicast_src_handler); #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ install_keyword("old_unicast_checksum", &vrrp_unicast_chksum_handler); #endif install_keyword("native_ipv6", &vrrp_native_ipv6_handler); install_keyword("state", &vrrp_state_handler); install_keyword("interface", &vrrp_int_handler); #ifdef _HAVE_VRF_ install_keyword("vrf", &vrrp_vrf_handler); #endif install_keyword("dont_track_primary", &vrrp_dont_track_handler); install_keyword("track_interface", &vrrp_track_if_handler); install_keyword("track_script", &vrrp_track_scr_handler); install_keyword("track_file", &vrrp_track_file_handler); #ifdef _WITH_TRACK_PROCESS_ install_keyword("track_process", &vrrp_track_process_handler); #endif #ifdef _WITH_BFD_ install_keyword("track_bfd", &vrrp_track_bfd_handler); #endif install_keyword("mcast_src_ip", &vrrp_srcip_handler); install_keyword("unicast_src_ip", &vrrp_unicast_srcip_handler); install_keyword("mcast_dst_ip", &vrrp_mcast_dstip_handler); install_keyword("track_src_ip", &vrrp_track_srcip_handler); install_keyword("virtual_router_id", &vrrp_vrid_handler); install_keyword("unicast_ttl", &vrrp_ttl_handler); install_keyword("version", &vrrp_version_handler); install_keyword("priority", &vrrp_prio_handler); install_keyword("advert_int", &vrrp_adv_handler); install_keyword("virtual_ipaddress", &vrrp_vip_handler); install_keyword("virtual_ipaddress_excluded", &vrrp_evip_handler); install_keyword("no_virtual_ipaddress", &vrrp_no_vip_handler); install_keyword("promote_secondaries", &vrrp_promote_secondaries_handler); #ifdef _WITH_LINKBEAT_ install_keyword("linkbeat_use_polling", &vrrp_linkbeat_handler); #endif install_keyword("v3_checksum_as_v2", &v3_checksum_as_v2); install_keyword("virtual_routes", &vrrp_vroutes_handler); install_keyword("virtual_rules", &vrrp_vrules_handler); install_keyword("accept", &vrrp_accept_handler); #ifdef _WITH_FIREWALL_ install_keyword("no_accept", &vrrp_no_accept_handler); #endif install_keyword("skip_check_adv_addr", &vrrp_skip_check_adv_addr_handler); install_keyword("strict_mode", &vrrp_strict_mode_handler); install_keyword("preempt", &vrrp_preempt_handler); install_keyword("nopreempt", &vrrp_nopreempt_handler); install_keyword("preempt_delay", &vrrp_preempt_delay_handler); install_keyword("debug", &vrrp_debug_handler); install_keyword_quoted("notify_backup", &vrrp_notify_backup_handler); install_keyword_quoted("notify_master", &vrrp_notify_master_handler); install_keyword_quoted("notify_fault", &vrrp_notify_fault_handler); install_keyword_quoted("notify_stop", &vrrp_notify_stop_handler); install_keyword_quoted("notify_deleted", &vrrp_notify_deleted_handler); install_keyword_quoted("notify", &vrrp_notify_handler); install_keyword_quoted("notify_master_rx_lower_pri", vrrp_notify_master_rx_lower_pri); install_keyword("smtp_alert", &vrrp_smtp_handler); install_keyword("notify_priority_changes", &vrrp_notify_priority_changes_handler); #ifdef _WITH_LVS_ install_keyword("lvs_sync_daemon_interface", &vrrp_lvs_syncd_handler); #endif install_keyword("garp_master_delay", &vrrp_garp_delay_handler); install_keyword("garp_master_refresh", &vrrp_garp_refresh_handler); install_keyword("garp_master_repeat", &vrrp_garp_rep_handler); install_keyword("garp_master_refresh_repeat", &vrrp_garp_refresh_rep_handler); install_keyword("garp_lower_prio_delay", &vrrp_garp_lower_prio_delay_handler); install_keyword("garp_lower_prio_repeat", &vrrp_garp_lower_prio_rep_handler); install_keyword("down_timer_adverts", &vrrp_down_timer_adverts_handler); install_keyword("timer_expired_backup", &vrrp_timer_expired_backup_handler); install_keyword("thread_timer_expired", &vrrp_thread_timer_expired_handler); // Added to match the release notes #ifdef _HAVE_VRRP_VMAC_ install_keyword("garp_extra_if", &vrrp_garp_extra_if_handler); install_keyword("vmac_garp_intvl", &vrrp_garp_extra_if_handler); /* Deprecated after v2.2.2 - incorrect keyword in commit 3dcd13c */ #endif install_keyword("lower_prio_no_advert", &vrrp_lower_prio_no_advert_handler); install_keyword("higher_prio_send_advert", &vrrp_higher_prio_send_advert_handler); install_keyword("owner_ignore_adverts", &vrrp_owner_ignore_adverts_handler); install_keyword("kernel_rx_buf_size", &kernel_rx_buf_size_handler); #if defined _WITH_VRRP_AUTH_ install_keyword("authentication", NULL); check_ptr = install_sublevel(VPP ¤t_vrrp); install_keyword("auth_type", &vrrp_auth_type_handler); install_keyword("auth_pass", &vrrp_auth_pass_handler); install_sublevel_end(check_ptr); #endif /* Script declarations */ install_keyword_root("vrrp_script", &vrrp_script_handler, active, VPP ¤t_vscr); install_keyword_quoted("script", &vrrp_vscript_script_handler); install_keyword("interval", &vrrp_vscript_interval_handler); install_keyword("timeout", &vrrp_vscript_timeout_handler); install_keyword("weight", &vrrp_vscript_weight_handler); install_keyword("rise", &vrrp_vscript_rise_handler); install_keyword("fall", &vrrp_vscript_fall_handler); install_keyword("user", &vrrp_vscript_user_handler); install_keyword("init_fail", &vrrp_vscript_init_fail_handler); install_level_end_handler(&vrrp_vscript_end_handler); #ifdef _WITH_TRACK_PROCESS_ /* Track process declarations */ install_keyword_root("vrrp_track_process", &vrrp_tprocess_handler, active, VPP ¤t_tp); install_keyword("process", &vrrp_tprocess_process_handler); install_keyword("param_match", vrrp_tprocess_match_handler); install_keyword("weight", &vrrp_tprocess_weight_handler); install_keyword("quorum", &vrrp_tprocess_quorum_handler); install_keyword("quorum_max", &vrrp_tprocess_quorum_max_handler); install_keyword("delay", &vrrp_tprocess_delay_handler); install_keyword("terminate_delay", &vrrp_tprocess_terminate_delay_handler); install_keyword("fork_delay", &vrrp_tprocess_fork_delay_handler); install_keyword("full_command", &vrrp_tprocess_full_handler); install_level_end_handler(&vrrp_tprocess_end_handler); #endif /* Interface up down delays */ install_keyword_root("interface_up_down_delays", &interface_up_down_delays_handler, active, NULL); } const vector_t * vrrp_init_keywords(void) { /* global definitions mapping */ init_global_keywords(reload); init_vrrp_keywords(true); #ifdef _WITH_LVS_ init_check_keywords(false); #endif #ifdef _WITH_BFD_ init_bfd_keywords(true); #endif add_track_file_keywords(true); cur_aggregation_group = 0; return keywords; } keepalived-2.3.3/keepalived/vrrp/Makefile.am0000664000175000017500000000357314303133343014456 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib AM_CPPFLAGS += $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libvrrp.a libvrrp_a_SOURCES = \ vrrp_daemon.c vrrp_print.c vrrp_data.c vrrp_parser.c \ vrrp.c vrrp_notify.c vrrp_scheduler.c vrrp_sync.c \ vrrp_arp.c vrrp_if.c vrrp_track.c vrrp_ipaddress.c \ vrrp_ndisc.c vrrp_if_config.c vrrp_static_track.c \ vrrp_iproute.c vrrp_iprule.c vrrp_ip_rule_route_parser.c libvrrp_a_SOURCES += ../include/vrrp_daemon.h libvrrp_a_LIBADD = EXTRA_libvrrp_a_SOURCES = if VMAC libvrrp_a_LIBADD += vrrp_vmac.o EXTRA_libvrrp_a_SOURCES += vrrp_vmac.c endif if VRRP_AUTH libvrrp_a_LIBADD += vrrp_ipsecah.o EXTRA_libvrrp_a_SOURCES += vrrp_ipsecah.c endif if WITH_DBUS libvrrp_a_LIBADD += vrrp_dbus.o EXTRA_libvrrp_a_SOURCES += vrrp_dbus.c endif if FIREWALL libvrrp_a_LIBADD += vrrp_firewall.o vrrp_firewall.o EXTRA_libvrrp_a_SOURCES += vrrp_firewall.c vrrp_firewall.c endif if IPTABLES libvrrp_a_LIBADD += vrrp_iptables.o vrrp_iptables_calls.o EXTRA_libvrrp_a_SOURCES += vrrp_iptables.c vrrp_iptables_calls.c endif if LIBIPSET libvrrp_a_LIBADD += vrrp_ipset.o EXTRA_libvrrp_a_SOURCES += vrrp_ipset.c endif if NFTABLES libvrrp_a_LIBADD += vrrp_nftables.o EXTRA_libvrrp_a_SOURCES += vrrp_nftables.c endif if SNMP_VRRP libvrrp_a_LIBADD += vrrp_snmp.o EXTRA_libvrrp_a_SOURCES += vrrp_snmp.c endif if WITH_JSON libvrrp_a_LIBADD += vrrp_json.o EXTRA_libvrrp_a_SOURCES += vrrp_json.c endif if NETWORK_MANAGER libvrrp_a_LIBADD += vrrp_vmac_nm.o EXTRA_libvrrp_a_SOURCES += vrrp_vmac_nm.c endif MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ keepalived-2.3.3/keepalived/vrrp/vrrp_iprule.c0000664000175000017500000006513314042743124015143 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: NETLINK IPv4 rules manipulation. * * Author: Chris Riley, * * 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) 2015 Chris Riley, * Copyright (C) 2016-2017 Alexandre Cassen, */ #include "config.h" /* global includes */ #include #include #include #include #include #include #if HAVE_DECL_FRA_IP_PROTO #include #include #endif #include /* local include */ #include "vrrp_iproute.h" #include "vrrp_iprule.h" #include "keepalived_netlink.h" #include "vrrp_data.h" #include "logger.h" #include "memory.h" #include "utils.h" #include "rttables.h" #include "vrrp_ip_rule_route_parser.h" #include "parser.h" #include "bitops.h" /* Since we will be adding and deleting rules in potentially random * orders due to master/backup transitions, we therefore need to * pre-allocate priorities to ensure the rules are added in a consistent * sequence. Really the configuration should specify a priority for each * rule to ensure they are configured in the order the user wants. */ #define RULE_START_PRIORITY 16384 static unsigned next_rule_priority_ipv4 = RULE_START_PRIORITY; static unsigned next_rule_priority_ipv6 = RULE_START_PRIORITY; /* Utility functions */ static inline bool rule_is_equal(const ip_rule_t *x, const ip_rule_t *y) { if (x->mask != y->mask || x->invert != y->invert || compare_ipaddress(x->from_addr, y->from_addr) || compare_ipaddress(x->to_addr, y->to_addr) || x->priority != y->priority || x->tos != y->tos || x->fwmark != y->fwmark || x->fwmask != y->fwmask || x->realms != y->realms || #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN x->suppress_prefix_len != y->suppress_prefix_len || #endif #if HAVE_DECL_FRA_SUPPRESS_IFGROUP x->suppress_group != y->suppress_group || #endif #if HAVE_DECL_FRA_TUN_ID x->tunnel_id != y->tunnel_id || #endif #if HAVE_DECL_FRA_UID_RANGE x->uid_range.start != y->uid_range.start || x->uid_range.end != y->uid_range.end || #endif #if HAVE_DECL_FRA_L3MDEV x->l3mdev != y->l3mdev || #endif x->iif != y->iif || x->oif != y->oif || #if HAVE_DECL_FRA_PROTOCOL x->protocol != y->protocol || #endif #if HAVE_DECL_FRA_IP_PROTO x->ip_proto != y->ip_proto || #endif #if HAVE_DECL_FRA_SPORT_RANGE x->src_port.start != y->src_port.start || x->src_port.end != y->src_port.end || #endif #if HAVE_DECL_FRA_DPORT_RANGE x->dst_port.start != y->dst_port.start || x->dst_port.end != y->dst_port.end || #endif x->goto_target != y->goto_target || x->table != y->table || x->action != y->action) return false; return true; } #if HAVE_DECL_FRA_IP_PROTO static int inet_proto_a2n(const char *buf) { struct protoent *pe; unsigned long proto_num; char *endptr; /* Skip white space */ buf += strspn(buf, WHITE_SPACE); if (!*buf || *buf == '-') return -1; proto_num = strtoul(buf, &endptr, 10); if (proto_num > INT8_MAX) return -1; if (!*endptr) return proto_num; pe = getprotobyname(buf); endprotoent(); if (pe) return pe->p_proto; return -1; } #endif /* Add/Delete IP rule to/from a specific IP/network */ static int netlink_rule(ip_rule_t *iprule, int cmd) { int status = 1; struct { struct nlmsghdr n; struct fib_rule_hdr frh; char buf[1024]; } req; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST; if (cmd != IPRULE_DEL) { req.n.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; req.n.nlmsg_type = RTM_NEWRULE; req.frh.action = FR_ACT_UNSPEC; } else { req.frh.action = FR_ACT_UNSPEC; req.n.nlmsg_type = RTM_DELRULE; } req.frh.table = RT_TABLE_UNSPEC; req.frh.flags = 0; req.frh.tos = iprule->tos; // Hex value - 0xnn <= 255, or name from rt_dsfield req.frh.family = iprule->family; if (iprule->action == FR_ACT_TO_TBL #if HAVE_DECL_FRA_L3MDEV && !iprule->l3mdev #endif ) { if (iprule->table < 256) // "Table" or "lookup" req.frh.table = iprule->table ? iprule->table & 0xff : RT_TABLE_MAIN; else { req.frh.table = RT_TABLE_UNSPEC; addattr32(&req.n, sizeof(req), FRA_TABLE, iprule->table); } } if (iprule->invert) req.frh.flags |= FIB_RULE_INVERT; // "not" /* Set rule entry */ if (iprule->from_addr) { // can be "default"/"any"/"all" - and to addr => bytelen == bitlen == 0 add_addr2req(&req.n, sizeof(req), FRA_SRC, iprule->from_addr); req.frh.src_len = iprule->from_addr->ifa.ifa_prefixlen; } if (iprule->to_addr) { add_addr2req(&req.n, sizeof(req), FRA_DST, iprule->to_addr); req.frh.dst_len = iprule->to_addr->ifa.ifa_prefixlen; } if (iprule->mask & IPRULE_BIT_PRIORITY) // "priority/order/preference" addattr32(&req.n, sizeof(req), FRA_PRIORITY, iprule->priority); if (iprule->mask & IPRULE_BIT_FWMARK) // "fwmark" addattr32(&req.n, sizeof(req), FRA_FWMARK, iprule->fwmark); if (iprule->mask & IPRULE_BIT_FWMASK) // "fwmark number followed by /nn" addattr32(&req.n, sizeof(req), FRA_FWMASK, iprule->fwmask); if (iprule->realms) // "realms u16[/u16] using rt_realms. after / is 16 msb (src), pre slash is 16 lsb (dest)" addattr32(&req.n, sizeof(req), FRA_FLOW, iprule->realms); #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN if (iprule->suppress_prefix_len != -1) // "suppress_prefixlength" - only valid if table != 0 addattr32(&req.n, sizeof(req), FRA_SUPPRESS_PREFIXLEN, iprule->suppress_prefix_len); #endif #if HAVE_DECL_FRA_SUPPRESS_IFGROUP if (iprule->mask & IPRULE_BIT_SUP_GROUP) // "suppress_ifgroup" or "sup_group" int32 - only valid if table !=0 addattr32(&req.n, sizeof(req), FRA_SUPPRESS_IFGROUP, iprule->suppress_group); #endif if (iprule->iif) // "dev/iif" addattr_l(&req.n, sizeof(req), FRA_IFNAME, iprule->iif, strlen(iprule->iif->ifname)+1); if (iprule->oif) // "oif" addattr_l(&req.n, sizeof(req), FRA_OIFNAME, iprule->oif, strlen(iprule->oif->ifname)+1); #if HAVE_DECL_FRA_TUN_ID if (iprule->tunnel_id) addattr64(&req.n, sizeof(req), FRA_TUN_ID, htobe64(iprule->tunnel_id)); #endif #if HAVE_DECL_FRA_UID_RANGE if (iprule->mask & IPRULE_BIT_UID_RANGE) addattr_l(&req.n, sizeof(req), FRA_UID_RANGE, &iprule->uid_range, sizeof(iprule->uid_range)); #endif #if HAVE_DECL_FRA_L3MDEV if (iprule->l3mdev) addattr8(&req.n, sizeof(req), FRA_L3MDEV, 1); #endif #if HAVE_DECL_FRA_PROTOCOL if (iprule->mask & IPRULE_BIT_PROTOCOL) addattr8(&req.n, sizeof(req), FRA_PROTOCOL, iprule->protocol); #endif #if HAVE_DECL_FRA_IP_PROTO if (iprule->mask & IPRULE_BIT_IP_PROTO) addattr8(&req.n, sizeof(req), FRA_IP_PROTO, iprule->ip_proto); #endif #if HAVE_DECL_FRA_SPORT_RANGE if (iprule->mask & IPRULE_BIT_SPORT_RANGE) addattr_l(&req.n, sizeof(req), FRA_SPORT_RANGE, &iprule->src_port, sizeof(iprule->src_port)); #endif #if HAVE_DECL_FRA_DPORT_RANGE if (iprule->mask & IPRULE_BIT_DPORT_RANGE) addattr_l(&req.n, sizeof(req), FRA_DPORT_RANGE, &iprule->dst_port, sizeof(iprule->dst_port)); #endif if (iprule->action == FR_ACT_GOTO) { // "goto" addattr32(&req.n, sizeof(req), FRA_GOTO, iprule->goto_target); req.frh.action = FR_ACT_GOTO; } req.frh.action = iprule->action; if (netlink_talk(&nl_cmd, &req.n) < 0) status = -1; return status; } void reinstate_static_rule(ip_rule_t *rule) { char buf[256]; rule->set = (netlink_rule(rule, IPRULE_ADD) > 0); format_iprule(rule, buf, sizeof(buf)); log_message(LOG_INFO, "Restoring deleted static rule %s", buf); } void netlink_rulelist(list_head_t *l, int cmd, bool force) { ip_rule_t *rule; /* No rules to add */ if (list_empty(l)) return; /* If force is set, we try to remove all the rules, but the * rule might not exist. That's not an error, so indicate not * to report such a situation */ if (force && cmd == IPRULE_DEL) netlink_error_ignore = ENOENT; list_for_each_entry(rule, l, e_list) { if (force || (cmd == IPRULE_ADD && !rule->set) || (cmd == IPRULE_DEL && rule->set)) { if (netlink_rule(rule, cmd) > 0) rule->set = (cmd == IPRULE_ADD); else rule->set = false; } } netlink_error_ignore = 0; } /* Rule dump/allocation */ void free_iprule(ip_rule_t *rule) { list_del_init(&rule->e_list); FREE_PTR(rule->from_addr); FREE_PTR(rule->to_addr); FREE(rule); } void free_iprule_list(list_head_t *l) { ip_rule_t *rule, *rule_tmp; list_for_each_entry_safe(rule, rule_tmp, l, e_list) free_iprule(rule); } void format_iprule(const ip_rule_t *rule, char *buf, size_t buf_len) { char *op = buf; char *buf_end = buf + buf_len; if (!rule->to_addr && !rule->from_addr && rule->family == AF_INET6) op += snprintf(op, (size_t)(buf_end - op), "inet6 "); if (rule->invert) op += snprintf(op, (size_t)(buf_end - op), "not "); if (rule->from_addr) op += snprintf(op, (size_t)(buf_end - op), "from %s", ipaddresstos(NULL, rule->from_addr)); else op += snprintf(op, (size_t)(buf_end - op), "from all" ); if (rule->to_addr) op += snprintf(op, (size_t)(buf_end - op), " to %s", ipaddresstos(NULL, rule->to_addr)); if (rule->mask & IPRULE_BIT_PRIORITY) op += snprintf(op, (size_t)(buf_end - op), " priority %u", rule->priority); op += snprintf(op, (size_t)(buf_end - op), " tos 0x%x", rule->tos); if (rule->mask & (IPRULE_BIT_FWMARK | IPRULE_BIT_FWMASK)) { op += snprintf(op, (size_t)(buf_end - op), " fwmark 0x%x", rule->fwmark); if (rule->mask & IPRULE_BIT_FWMASK && rule->fwmask != 0xffffffff) op += snprintf(op, (size_t)(buf_end - op), "/0x%x", rule->fwmask); } if (rule->iif) op += snprintf(op, (size_t)(buf_end - op), " iif %s", rule->iif->ifname); if (rule->oif) op += snprintf(op, (size_t)(buf_end - op), " oif %s", rule->oif->ifname); #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN if (rule->suppress_prefix_len != -1) op += snprintf(op, (size_t)(buf_end - op), " suppress_prefixlen %" PRIi32, rule->suppress_prefix_len); #endif #if HAVE_DECL_FRA_SUPPRESS_IFGROUP if (rule->mask & IPRULE_BIT_SUP_GROUP) op += snprintf(op, (size_t)(buf_end - op), " suppress_ifgroup %" PRIu32, rule->suppress_group); #endif #if HAVE_DECL_FRA_TUN_ID if (rule->tunnel_id) op += snprintf(op, (size_t)(buf_end - op), " tunnel-id %" PRIu64, rule->tunnel_id); #endif #if HAVE_DECL_FRA_UID_RANGE if (rule->mask & IPRULE_BIT_UID_RANGE) op += snprintf(op, (size_t)(buf_end - op), " uidrange %" PRIu32 "-%" PRIu32, rule->uid_range.start, rule->uid_range.end); #endif #if HAVE_DECL_FRA_L3MDEV if (rule->l3mdev) op += snprintf(op, (size_t)(buf_end - op), " l3mdev"); #endif #if HAVE_DECL_FRA_PROTOCOL if (rule->mask & IPRULE_BIT_PROTOCOL) op += snprintf(op, (size_t)(buf_end - op), " protocol %u", rule->protocol); #endif #if HAVE_DECL_FRA_IP_PROTO if (rule->mask & IPRULE_BIT_IP_PROTO) op += snprintf(op, (size_t)(buf_end - op), " ipproto %u", rule->ip_proto); #endif #if HAVE_DECL_FRA_SPORT_RANGE if (rule->mask & IPRULE_BIT_SPORT_RANGE) op += snprintf(op, (size_t)(buf_end - op), " sport %hu-%hu", rule->src_port.start, rule->src_port.end); #endif #if HAVE_DECL_FRA_DPORT_RANGE if (rule->mask & IPRULE_BIT_DPORT_RANGE) op += snprintf(op, (size_t)(buf_end - op), " dport %hu-%hu", rule->dst_port.start, rule->dst_port.end); #endif if (rule->realms) op += snprintf(op, (size_t)(buf_end - op), " realms %" PRIu32 "/%u", rule->realms >> 16, rule->realms & 0xffff); if (rule->action == FR_ACT_TO_TBL) op += snprintf(op, (size_t)(buf_end - op), " lookup %u", rule->table); else if (rule->action == FR_ACT_GOTO) op += snprintf(op, (size_t)(buf_end - op), " goto %u", rule->goto_target); else if (rule->action == FR_ACT_NOP) op += snprintf(op, (size_t)(buf_end - op), " nop"); else op += snprintf(op, (size_t)(buf_end - op), " type %s", get_rttables_rtntype(rule->action)); if (rule->dont_track) op += snprintf(op, (size_t)(buf_end - op), " no_track"); if (rule->track_group) op += snprintf(op, (size_t)(buf_end - op), " track_group %s", rule->track_group->gname); } void dump_iprule(FILE *fp, const ip_rule_t *rule) { char *buf = MALLOC(RULE_BUF_SIZE); format_iprule(rule, buf, RULE_BUF_SIZE); conf_write(fp, " %s", buf); FREE(buf); } void dump_iprule_list(FILE *fp, const list_head_t *l) { ip_rule_t *rule; list_for_each_entry(rule, l, e_list) dump_iprule(fp, rule); } void alloc_rule(list_head_t *rule_list, const vector_t *strvec, __attribute__((unused)) bool allow_track_group) { ip_rule_t *new; const char *str; unsigned int i = 0; unsigned long val, val1; unsigned val_unsigned; uint32_t uval32; uint8_t uval8; int family = AF_UNSPEC; interface_t *ifp; char *end; bool table_option = false; PMALLOC(new); if (!new) { log_message(LOG_INFO, "Unable to allocate new ip_rule"); return; } INIT_LIST_HEAD(&new->e_list); new->action = FR_ACT_UNSPEC; #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN new->suppress_prefix_len = -1; #endif /* FMT parse */ while (i < vector_size(strvec)) { str = strvec_slot(strvec, i); /* Check if inet4/6 specified */ if (!strcmp(str, "inet6")) { if (family == AF_UNSPEC) family = AF_INET6; else if (family != AF_INET6) { report_config_error(CONFIG_GENERAL_ERROR, "inet6 specified for IPv4 rule"); goto err; } i++; } else if (!strcmp(str, "inet")) { if (family == AF_UNSPEC) family = AF_INET; else if (family != AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "inet specified for IPv6 rule"); goto err; } i++; } else if (!strcmp(str, "from")) { if (new->from_addr) FREE(new->from_addr); new->from_addr = parse_route(strvec_slot(strvec, ++i)); if (!new->from_addr) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid rule from address %s", strvec_slot(strvec, i)); goto err; } if (family == AF_UNSPEC) family = new->from_addr->ifa.ifa_family; else if (new->from_addr->ifa.ifa_family != family) { report_config_error(CONFIG_GENERAL_ERROR, "rule specification has mixed IPv4 and IPv6"); goto err; } } else if (!strcmp(str, "to")) { if (new->to_addr) FREE(new->to_addr); new->to_addr = parse_route(strvec_slot(strvec, ++i)); if (!new->to_addr) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid rule to address %s", strvec_slot(strvec, i)); goto err; } if (family == AF_UNSPEC) family = new->to_addr->ifa.ifa_family; else if (new->to_addr->ifa.ifa_family != family) { report_config_error(CONFIG_GENERAL_ERROR, "rule specification has mixed IPv4 and IPv6"); goto err; } } else if (!strcmp(str, "table") || !strcmp(str, "lookup")) { if (!find_rttables_table(strvec_slot(strvec, ++i), &uval32)) { report_config_error(CONFIG_GENERAL_ERROR, "Routing table %s not found for rule", strvec_slot(strvec, i)); goto err; } if (uval32 == 0) { report_config_error(CONFIG_GENERAL_ERROR, "Table 0 is not valid"); goto err; } new->table = uval32; if (new->action != FR_ACT_UNSPEC) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify more than one of table/nop/goto/blackhole/prohibit/unreachable for rule"); goto err; } new->action = FR_ACT_TO_TBL; } else if (!strcmp(str,"not")) new->invert = true; else if (!strcmp(str, "preference") || !strcmp(str, "order") || !strcmp(str, "priority")) { if (!read_unsigned_base_strvec(strvec, ++i, 0, &val_unsigned, 0, UINT32_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid rule preference %s specified", str); goto err; } new->priority = (uint32_t)val_unsigned; new->mask |= IPRULE_BIT_PRIORITY; } else if (!strcmp(str, "tos") || !strcmp(str, "dsfield")) { if (!find_rttables_dsfield(strvec_slot(strvec, ++i), &uval8)) { report_config_error(CONFIG_GENERAL_ERROR, "TOS value %s is invalid", strvec_slot(strvec, i)); goto err; } new->tos = uval8; } else if (!strcmp(str, "fwmark")) { str = strvec_slot(strvec, ++i); str += strspn(str, WHITE_SPACE); if (str[0] == '-') goto fwmark_err; val = strtoul(str, &end, 0); if (val > UINT32_MAX) goto fwmark_err; if (*end == '/') { if (isspace(end[1]) || end[1] == '-') goto fwmark_err; val1 = strtoul(end + 1, &end, 0); if (val1 > UINT32_MAX) goto fwmark_err; new->mask |= IPRULE_BIT_FWMASK; } else val1 = 0; if (*end) goto fwmark_err; new->fwmark = (uint32_t)val; new->fwmask = (uint32_t)val1; new->mask |= IPRULE_BIT_FWMARK; if (false) { fwmark_err: report_config_error(CONFIG_GENERAL_ERROR, "Invalid rule fwmark %s specified", str); new->mask &= (uint32_t)~IPRULE_BIT_FWMASK; goto err; } } else if (!strcmp(str, "realms")) { str = strvec_slot(strvec, ++i); if (get_realms(&uval32, str)) { report_config_error(CONFIG_GENERAL_ERROR, "invalid realms %s for rule", strvec_slot(strvec, i)); goto err; } new->realms = uval32; table_option = true; if (family == AF_UNSPEC) family = AF_INET; else if (family != AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "realms is only valid for IPv4"); goto err; } } #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN else if (!strcmp(str, "suppress_prefixlength") || !strcmp(str, "sup_pl")) { if (!read_unsigned_strvec(strvec, ++i, &val_unsigned, 0, INT32_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid suppress_prefixlength %s specified", str); goto err; } new->suppress_prefix_len = (int32_t)val_unsigned; table_option = true; } #endif #if HAVE_DECL_FRA_SUPPRESS_IFGROUP else if (!strcmp(str, "suppress_ifgroup") || !strcmp(str, "sup_group")) { if (!find_rttables_group(strvec_slot(strvec, ++i), &uval32)) { report_config_error(CONFIG_GENERAL_ERROR, "suppress_group %s is invalid", strvec_slot(strvec, i)); goto err; } new->suppress_group = uval32; new->mask |= IPRULE_BIT_SUP_GROUP; table_option = true; } #endif else if (!strcmp(str, "dev") || !strcmp(str, "iif")) { str = strvec_slot(strvec, ++i); ifp = if_get_by_ifname(str, IF_CREATE_IF_DYNAMIC); if (!ifp) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - interface %s for rule doesn't exist", str); goto err; } new->iif = ifp; } else if (!strcmp(str, "oif")) { str = strvec_slot(strvec, ++i); ifp = if_get_by_ifname(str, IF_CREATE_IF_DYNAMIC); if (!ifp) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - interface %s for rule doesn't exist", str); goto err; } new->oif = ifp; } #if HAVE_DECL_FRA_TUN_ID else if (!strcmp(str, "tunnel-id")) { uint64_t val64; if (!read_unsigned64_strvec(strvec, ++i, &val64, 0, UINT64_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid tunnel-id %s specified", str); goto err; } new->tunnel_id = val64; } #endif #if HAVE_DECL_FRA_UID_RANGE else if (!strcmp(str, "uidrange")) { uint32_t range_start, range_end; if (sscanf(strvec_slot(strvec, ++i), "%" PRIu32 "-%" PRIu32, &range_start, &range_end) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid uidrange %s specified", str); goto err; } new->mask |= IPRULE_BIT_UID_RANGE; new->uid_range.start = range_start; new->uid_range.end = range_end; } #endif #if HAVE_DECL_FRA_L3MDEV else if (!strcmp(str, "l3mdev")) { new->l3mdev = true; if (new->action != FR_ACT_UNSPEC) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify l3mdev with other action"); goto err; } new->action = FR_ACT_TO_TBL; } #endif #if HAVE_DECL_FRA_PROTOCOL else if (!strcmp(str, "protocol")) { if (!read_unsigned_strvec(strvec, ++i, &val_unsigned, 0, UINT8_MAX, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid protocol %s", strvec_slot(strvec, i)); else { new->protocol = val_unsigned; new->mask |= IPRULE_BIT_PROTOCOL; } } #endif #if HAVE_DECL_FRA_IP_PROTO else if (!strcmp(str, "ipproto")) { int ip_proto = inet_proto_a2n(strvec_slot(strvec, ++i)); if (ip_proto < 0 || ip_proto > UINT8_MAX) report_config_error(CONFIG_GENERAL_ERROR, "Invalid ipproto %s", strvec_slot(strvec, i)); else { new->ip_proto = ip_proto; new->mask |= IPRULE_BIT_IP_PROTO; } } #endif #if HAVE_DECL_FRA_SPORT_RANGE else if (!strcmp(str, "sport")) { struct fib_rule_port_range sport; int ret; ret = sscanf(strvec_slot(strvec, ++i), "%hu-%hu", &sport.start, &sport.end); if (ret == 1) sport.end = sport.start; if (ret != 2) report_config_error(CONFIG_GENERAL_ERROR, "invalid sport range %s", strvec_slot(strvec, i)); else { new->src_port = sport; new->mask |= IPRULE_BIT_SPORT_RANGE; } } #endif #if HAVE_DECL_FRA_DPORT_RANGE else if (!strcmp(str, "dport")) { struct fib_rule_port_range dport; int ret; ret = sscanf(strvec_slot(strvec, ++i), "%hu-%hu", &dport.start, &dport.end); if (ret == 1) dport.end = dport.start; if (ret != 2) report_config_error(CONFIG_GENERAL_ERROR, "invalid dport range %s", strvec_slot(strvec, i)); else { new->dst_port = dport; new->mask |= IPRULE_BIT_DPORT_RANGE; } } #endif else if (!strcmp(str, "no_track")) new->dont_track = true; else if (allow_track_group && !strcmp(str, "track_group")) { i++; if (new->track_group) { report_config_error(CONFIG_GENERAL_ERROR, "track_group %s is a duplicate", strvec_slot(strvec, i)); break; } if (!(new->track_group = static_track_group_find(strvec_slot(strvec, i)))) report_config_error(CONFIG_GENERAL_ERROR, "track_group %s not found", strvec_slot(strvec, i)); } else { uint8_t action = FR_ACT_UNSPEC; if (!strcmp(str, "type")) str = strvec_slot(strvec, ++i); if (!strcmp(str, "goto")) { if (!read_unsigned_strvec(strvec, ++i, &val_unsigned, 0, UINT32_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid target %s specified", str); goto err; } new->goto_target = (uint32_t)val_unsigned; action = FR_ACT_GOTO; } else if (!strcmp(str, "nop")) { action = FR_ACT_NOP; } else if (find_rttables_rtntype(str, &action)) { if (action == RTN_BLACKHOLE) action = FR_ACT_BLACKHOLE; else if (action == RTN_UNREACHABLE) action = FR_ACT_UNREACHABLE; else if (action == RTN_PROHIBIT) action = FR_ACT_PROHIBIT; else { report_config_error(CONFIG_GENERAL_ERROR, "Invalid rule action %s", str); goto err; } } else { report_config_error(CONFIG_GENERAL_ERROR, "Unknown rule option %s", str); goto err; } if (new->action != FR_ACT_UNSPEC) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot specify more than one of table/nop/goto/blackhole/prohibit/unreachable/l3mdev for rule"); goto err; } new->action = action; } i++; } if (new->action == FR_ACT_GOTO) { if (new->mask & IPRULE_BIT_PRIORITY) { if (new->priority >= new->goto_target) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid rule - preference %u >= goto target %u", new->priority, new->goto_target); goto err; } } else { report_config_error(CONFIG_GENERAL_ERROR, "Invalid rule - goto target %u specified without preference", new->goto_target); goto err; } } if (new->action == FR_ACT_UNSPEC) { report_config_error(CONFIG_GENERAL_ERROR, "No action specified for rule - ignoring"); goto err; } if (new->action != FR_ACT_TO_TBL && table_option) { report_config_error(CONFIG_GENERAL_ERROR, "suppressor/realm specified for non table action - skipping"); goto err; } #if HAVE_DECL_FRA_L3MDEV if (new->table && new->l3mdev) { report_config_error(CONFIG_GENERAL_ERROR, "table cannot be specified for l3mdev rules"); goto err; } #endif #if HAVE_DECL_FRA_PROTOCOL if (!new->dont_track) { if ((new->mask & IPRULE_BIT_PROTOCOL) && new->protocol != RTPROT_KEEPALIVED) report_config_error(CONFIG_GENERAL_ERROR, "Rule cannot be tracked if protocol is not RTPROT_KEEPALIVED(%d), resetting protocol", RTPROT_KEEPALIVED); new->protocol = RTPROT_KEEPALIVED; new->mask |= IPRULE_BIT_PROTOCOL; } #endif if (new->track_group && !new->iif) { report_config_error(CONFIG_GENERAL_ERROR, "Static rule cannot have track_group if dev/iif not specified"); new->track_group = NULL; } new->family = (family == AF_UNSPEC) ? AF_INET : family; if (new->to_addr && new->to_addr->ifa.ifa_family == AF_UNSPEC) new->to_addr->ifa.ifa_family = new->family; if (new->from_addr && new->from_addr->ifa.ifa_family == AF_UNSPEC) new->from_addr->ifa.ifa_family = new->family; if (!(new->mask & IPRULE_BIT_PRIORITY)) { new->priority = new->family == AF_INET ? next_rule_priority_ipv4-- : next_rule_priority_ipv6--; new->mask |= IPRULE_BIT_PRIORITY; report_config_error(CONFIG_GENERAL_ERROR, "Rule has no preference specified - setting to %u. This is probably not what you want.", new->priority); } list_add_tail(&new->e_list, rule_list); return; err: FREE_PTR(new->to_addr); FREE_PTR(new->from_addr); FREE_PTR(new); } /* Try to find a rule in a list */ static bool rule_exist(list_head_t *l, ip_rule_t *rule) { ip_rule_t *ip_rule; list_for_each_entry(ip_rule, l, e_list) { if (rule_is_equal(ip_rule, rule)) { ip_rule->set = rule->set; return true; } } return false; } /* Clear diff rules */ void clear_diff_rules(list_head_t *l, list_head_t *n) { ip_rule_t *rule; char from_addr[IPADDRESSTOS_BUF_LEN]; char to_addr[IPADDRESSTOS_BUF_LEN]; /* No rule in previous conf */ if (list_empty(l)) return; /* All rules removed */ if (list_empty(n)) { log_message(LOG_INFO, "Removing a VirtualRule block"); netlink_rulelist(l, IPRULE_DEL, false); return; } list_for_each_entry(rule, l, e_list) { if (!rule_exist(n, rule) && rule->set) { if (__test_bit(LOG_DETAIL_BIT, &debug)) { if (rule->from_addr) ipaddresstos(from_addr, rule->from_addr); if (rule->to_addr) ipaddresstos(to_addr, rule->to_addr); log_message(LOG_INFO, "ip rule from %s%s%s removed", rule->from_addr ? from_addr : "all", rule->to_addr ? " to " : "", rule->to_addr ? to_addr : ""); } netlink_rule(rule, IPRULE_DEL); } } } /* Diff conf handler */ void clear_diff_static_rules(void) { clear_diff_rules(&old_vrrp_data->static_rules, &vrrp_data->static_rules); } void reset_next_rule_priority(void) { next_rule_priority_ipv4 = RULE_START_PRIORITY; next_rule_priority_ipv6 = RULE_START_PRIORITY; } keepalived-2.3.3/keepalived/vrrp/vrrp_dbus.c0000664000175000017500000010015014706175171014575 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: DBus server thread for VRRP * * Author: Alexandre Cassen, * * 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) 2016-2017 Alexandre Cassen, */ /* See https://git.gnome.org/browse/glib/tree/gio/tests/fdbus-example-server.c * and https://developer.gnome.org/gio/stable/GDBusConnection.html#gdbus-server * for examples of coding. * * Create a general /org/keepalived/Vrrp1/Vrrp DBus * object and a /org/keepalived/Vrrp1/Instance/#interface#/#group# object for * each VRRP instance. * Interface org.keepalived.Vrrp1.Vrrp implements methods PrintData, * PrintStats and signal VrrpStopped. * Interface com.keepalived.Vrrp1.Instance implements method SendGarp * (sends a single Gratuitous ARP from the given Instance), * signal VrrpStatusChange, and properties Name and State (retrievable * through calls to org.freedesktop.DBus.Properties.Get) * * Interface files need to be installed in /usr/share/dbus-1/interfaces/ * A policy file, which determines who has access to the service, is * installed in /etc/dbus-1/system.d/. Sources for the policy and interface * files are in keepalived/dbus. * * To test the DBus service run a command like: dbus-send --system --dest=org.keepalived.Vrrp1 --print-reply object interface.method type:argument * e.g. * dbus-send --system --dest=org.keepalived.Vrrp1 --print-reply /org/keepalived/Vrrp1/Vrrp org.keepalived.Vrrp1.Vrrp.PrintData * or * dbus-send --system --dest=org.keepalived.Vrrp1 --print-reply /org/keepalived/Vrrp1/Instance/eth0/1/IPv4 org.freedesktop.DBus.Properties.Get string:'org.keepalived.Vrrp1.Instance' string:'State' * * To monitor signals, run: * dbus-monitor --system type='signal' * * d-feet is a useful program for interfacing with DBus */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "vrrp_dbus.h" #include "vrrp_data.h" #include "vrrp_print.h" #include "global_data.h" #include "main.h" #include "logger.h" #include "utils.h" #ifdef THREAD_DUMP #include "scheduler.h" #endif typedef enum dbus_action { DBUS_ACTION_NONE, DBUS_PRINT_DATA, DBUS_PRINT_STATS, DBUS_PRINT_STATS_CLEAR, DBUS_RELOAD, #ifdef _WITH_DBUS_CREATE_INSTANCE_ DBUS_CREATE_INSTANCE, DBUS_DESTROY_INSTANCE, #endif DBUS_SEND_GARP, DBUS_GET_NAME, DBUS_GET_STATUS, } dbus_action_t; typedef enum dbus_error { DBUS_SUCCESS, DBUS_INTERFACE_NOT_FOUND, DBUS_OBJECT_ALREADY_EXISTS, DBUS_INTERFACE_TOO_LONG, DBUS_INSTANCE_NOT_FOUND, } dbus_error_t; typedef struct dbus_queue_ent { dbus_action_t action; dbus_error_t reply; char *ifname; uint8_t vrid; uint8_t family; GVariant *args; } dbus_queue_ent_t; typedef struct dbus_files { const gchar *interface_file; const gchar *instance_interface_file; } dbus_files_t; #define DBUS_SERVICE_NAME "org.keepalived.Vrrp1" #define DBUS_VRRP_INTERFACE "org.keepalived.Vrrp1.Vrrp" #define DBUS_VRRP_OBJECT_ROOT "/org/keepalived/Vrrp1" #define DBUS_VRRP_INSTANCE_PATH_DEFAULT_LENGTH 8 #define DBUS_VRRP_INSTANCE_INTERFACE "org.keepalived.Vrrp1.Instance" #define DBUS_VRRP_INTERFACE_FILE_PATH DBUS_DATADIR "/dbus-1/interfaces/org.keepalived.Vrrp1.Vrrp.xml" #define DBUS_VRRP_INSTANCE_INTERFACE_FILE_PATH DBUS_DATADIR "/dbus-1/interfaces/org.keepalived.Vrrp1.Instance.xml" static bool dbus_running; static bool dbus_startup_completed; static pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t startup_cond = PTHREAD_COND_INITIALIZER; /* Global file variables */ const char *dbus_no_interface_name = "none"; static GDBusNodeInfo *vrrp_introspection_data = NULL; static GDBusNodeInfo *vrrp_instance_introspection_data = NULL; static GDBusConnection *global_connection; static GHashTable *objects; static GMainLoop *loop; /* Data passing between main vrrp thread and dbus thread */ dbus_queue_ent_t *ent_ptr; static int dbus_in_pipe[2] = { -1 }; // [0] == -1 indicates pipe is closed static int dbus_out_pipe[2] = { -1 }; // Ditto static sem_t thread_end; /* The only characters that are valid in a dbus path are A-Z, a-z, 0-9, _ */ static char * set_valid_path(char *valid_path, const char *path) { const char *str_in; char *str_out; for (str_in = path, str_out = valid_path; *str_in; str_in++, str_out++) { if (!isalnum(*str_in)) *str_out = '_'; else *str_out = *str_in; } *str_out = '\0'; return valid_path; } static bool __attribute__ ((pure)) valid_path_cmp(const char *path, const char *valid_path) { for ( ; *path && *valid_path; path++, valid_path++) { if (!isalnum(*path)) { if (*valid_path != '_') return true; } else if (*path != *valid_path) return true; } return *path != *valid_path; } static const char * family_str(int family) { if (family == AF_INET) return "IPv4"; if (family == AF_INET6) return "IPv6"; return "None"; } static const char * state_str(int state) { switch (state) { case VRRP_STATE_INIT: return "Init"; case VRRP_STATE_BACK: return "Backup"; case VRRP_STATE_MAST: return "Master"; case VRRP_STATE_FAULT: return "Fault"; case VRRP_STATE_STOP: return "Stop"; case VRRP_STATE_DELETED: return "Deleted"; } return "Unknown"; } static vrrp_t * __attribute__ ((pure)) get_vrrp_instance(const char *ifname, int vrid, int family) { vrrp_t *vrrp; bool no_if = !strcmp(ifname, dbus_no_interface_name); list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->vrid == vrid && vrrp->family == family) { if (no_if) { if (!vrrp->ifp) return vrrp; } else if (vrrp->ifp && !valid_path_cmp(VRRP_CONFIGURED_IFP(vrrp)->ifname, ifname)) return vrrp; } } return NULL; } static gboolean unregister_object(const void * const key, gpointer value, __attribute__((unused)) gpointer user_data) { if (g_hash_table_remove(objects, key)) return g_dbus_connection_unregister_object(global_connection, GPOINTER_TO_UINT(value)); return false; } static gboolean remove_object(__attribute__((unused)) gpointer key, gpointer value, __attribute__((unused)) gpointer user_data) { return g_dbus_connection_unregister_object(global_connection, GPOINTER_TO_UINT(value)); } static gchar * __attribute__ ((malloc)) dbus_object_create_path_vrrp(void) { return g_strconcat(DBUS_VRRP_OBJECT_ROOT, global_data->network_namespace ? "/" : "", global_data->network_namespace ? global_data->network_namespace : "", global_data->instance_name ? "/" : "", global_data->instance_name ? global_data->instance_name : "", "/Vrrp", NULL); } static gchar * __attribute__ ((malloc)) dbus_object_create_path_instance(const gchar *interface, int vrid, sa_family_t family) { gchar *object_path; char standardized_name[sizeof (PTR_CAST(vrrp_t, NULL))->ifp->ifname]; gchar *vrid_str = g_strdup_printf("%d", vrid); set_valid_path(standardized_name, interface); object_path = g_strconcat(DBUS_VRRP_OBJECT_ROOT, global_data->network_namespace ? "/" : "", global_data->network_namespace ? global_data->network_namespace : "", global_data->instance_name ? "/" : "", global_data->instance_name ? global_data->instance_name : "", "/Instance/", standardized_name, "/", vrid_str, "/", family_str(family), NULL); g_free(vrid_str); return object_path; } static dbus_queue_ent_t * process_method_call(dbus_queue_ent_t *ent) { ssize_t ret; char buf = 0; if (!ent) return NULL; /* Tell the main thread that a queue entry is waiting. Any data works */ ent_ptr = ent; if (write(dbus_in_pipe[1], &buf, 1) != 1) log_message(LOG_INFO, "Write from DBus thread to main thread failed"); /* Wait for a response */ while ((ret = read(dbus_out_pipe[0], &buf, 1)) == -1 && check_EINTR(errno)) log_message(LOG_INFO, "dbus_out_pipe read returned EINTR"); if (ret == -1) log_message(LOG_INFO, "DBus response read error - errno = %d", errno); #ifdef DBUS_DEBUG if (ent->reply != DBUS_SUCCESS) { char *iname; if (ent->reply == DBUS_INTERFACE_NOT_FOUND) log_message(LOG_INFO, "Unable to find DBus requested instance %s/%d/%s", ent->ifname, ent->vrid, family_str(ent->family)); else if (ent->reply == DBUS_OBJECT_ALREADY_EXISTS) log_message(LOG_INFO, "Unable to create DBus requested object with instance %s/%d/%s", ent->ifname, ent->vrid, family_str(ent->family)); else if (ent->reply == DBUS_INSTANCE_NOT_FOUND) { g_variant_get(ent->args, "(s)", &iname); log_message(LOG_INFO, "Unable to find DBus requested instance %s", iname); } else log_message(LOG_INFO, "Unknown DBus reply %d", ent->reply); } #endif return ent; } static void get_interface_ids(const gchar *object_path, gchar *interface, uint8_t *vrid, uint8_t *family) { int path_length = DBUS_VRRP_INSTANCE_PATH_DEFAULT_LENGTH; gchar **dirs; char *endptr; if(global_data->network_namespace) path_length++; if(global_data->instance_name) path_length++; /* object_path will have interface, vrid and family as * the third to last, second to last and last levels */ dirs = g_strsplit(object_path, "/", path_length); strcpy(interface, dirs[path_length-3]); *vrid = (uint8_t)strtoul(dirs[path_length-2], &endptr, 10); if (*endptr) log_message(LOG_INFO, "Dbus unexpected characters '%s' at end of number '%s'", endptr, dirs[path_length-2]); *family = !g_strcmp0(dirs[path_length-1], "IPv4") ? AF_INET : !g_strcmp0(dirs[path_length-1], "IPv6") ? AF_INET6 : AF_UNSPEC; /* We are finished with all the object_path strings now */ g_strfreev(dirs); } /* handles reply to org.freedesktop.DBus.Properties.Get method on any object*/ static GVariant * handle_get_property(__attribute__((unused)) GDBusConnection *connection, __attribute__((unused)) const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, __attribute__((unused)) gpointer user_data) { GVariant *ret = NULL; dbus_queue_ent_t ent; char ifname_str[sizeof (PTR_CAST(vrrp_t, NULL))->ifp->ifname]; int action; if (g_strcmp0(interface_name, DBUS_VRRP_INSTANCE_INTERFACE)) { log_message(LOG_INFO, "Interface %s has not been implemented yet", interface_name); return NULL; } if (!g_strcmp0(property_name, "Name")) action = DBUS_GET_NAME; else if (!g_strcmp0(property_name, "State")) action = DBUS_GET_STATUS; else { log_message(LOG_INFO, "Property %s does not exist", property_name); return NULL; } get_interface_ids(object_path, ifname_str, &ent.vrid, &ent.family); ent.action = action; ent.ifname = ifname_str; ent.args = NULL; process_method_call(&ent); if (ent.reply == DBUS_SUCCESS) ret = ent.args; else g_set_error(error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Instance '%s/%d/%s' not found", ifname_str, ent.vrid, family_str(ent.family)); return ret; } /* handles method_calls on any object */ static void handle_method_call(__attribute__((unused)) GDBusConnection *connection, __attribute__((unused)) const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, #ifndef _WITH_DBUS_CREATE_INSTANCE_ __attribute__((unused)) #endif GVariant *parameters, GDBusMethodInvocation *invocation, __attribute__((unused)) gpointer user_data) { #ifdef _WITH_DBUS_CREATE_INSTANCE_ char *iname; char *ifname; size_t len; unsigned family; #endif dbus_queue_ent_t ent; char ifname_str[sizeof (PTR_CAST(vrrp_t, NULL))->ifp->ifname]; if (!g_strcmp0(interface_name, DBUS_VRRP_INTERFACE)) { if (!g_strcmp0(method_name, "PrintData")) { ent.action = DBUS_PRINT_DATA; process_method_call(&ent); g_dbus_method_invocation_return_value(invocation, NULL); } else if (g_strcmp0(method_name, "PrintStats") == 0) { ent.action = DBUS_PRINT_STATS; process_method_call(&ent); g_dbus_method_invocation_return_value(invocation, NULL); } else if (g_strcmp0(method_name, "PrintStatsClear") == 0) { ent.action = DBUS_PRINT_STATS_CLEAR; process_method_call(&ent); g_dbus_method_invocation_return_value(invocation, NULL); } else if (g_strcmp0(method_name, "ReloadConfig") == 0) { g_dbus_method_invocation_return_value(invocation, NULL); kill(getppid(), SIGHUP); } #ifdef _WITH_DBUS_CREATE_INSTANCE_ else if (g_strcmp0(method_name, "CreateInstance") == 0) { g_variant_get(parameters, "(ssuu)", &iname, &ifname, &ent.vrid, &family); len = strlen(ifname); if (len == 0 || len >= IFNAMSIZ) { log_message(LOG_INFO, "Interface name '%s' too long for CreateInstance", ifname); g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Interface name empty or too long"); return; } ent.action = DBUS_CREATE_INSTANCE; ent.ifname = ifname; ent.family = family == 4 ? AF_INET : family == 6 ? AF_INET6 : AF_UNSPEC; ent.args = g_variant_new("(s)", iname); process_method_call(&ent); g_variant_unref(ent.args); g_dbus_method_invocation_return_value(invocation, NULL); } else if (g_strcmp0(method_name, "DestroyInstance") == 0) { // TODO - this should be on the instance ent.action = DBUS_DESTROY_INSTANCE; ent.args = parameters; process_method_call(&ent); if (ent.reply == DBUS_INSTANCE_NOT_FOUND) { g_variant_get(parameters, "(s)", &iname); g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Instance '%s' not found", iname); } else g_dbus_method_invocation_return_value(invocation, NULL); } #endif else { log_message(LOG_INFO, "Method %s has not been implemented yet", method_name); g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, "Method not implemented"); } return; } if (!g_strcmp0(interface_name, DBUS_VRRP_INSTANCE_INTERFACE)) { if (!g_strcmp0(method_name, "SendGarp")) { get_interface_ids(object_path, ifname_str, &ent.vrid, &ent.family); ent.action = DBUS_SEND_GARP; ent.ifname = ifname_str; process_method_call(&ent); if (ent.reply == DBUS_INTERFACE_NOT_FOUND) g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Instance '%s/%d/%s' not found", ifname_str, ent.vrid, family_str(ent.family)); else g_dbus_method_invocation_return_value(invocation, NULL); } else { log_message(LOG_INFO, "Method %s has not been implemented yet", method_name); g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, "Method not implemented"); } return; } log_message(LOG_INFO, "Interface %s has not been implemented yet", interface_name); g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, "Interface not implemented"); } static const GDBusInterfaceVTable interface_vtable = { handle_method_call, handle_get_property, NULL, /* handle_set_property is null because we have no writeable property */ {0} }; static int dbus_create_object_params(const char *instance_name, const char *interface_name, int vrid, sa_family_t family, bool log_success) { gchar *object_path; GError *local_error = NULL; if (g_hash_table_lookup(objects, instance_name)) { log_message(LOG_INFO, "An object for instance %s already exists", instance_name); return DBUS_OBJECT_ALREADY_EXISTS; } object_path = dbus_object_create_path_instance(interface_name, vrid, family); guint instance = g_dbus_connection_register_object(global_connection, object_path, vrrp_instance_introspection_data->interfaces[0], &interface_vtable, NULL, NULL, &local_error); if (local_error != NULL) { log_message(LOG_INFO, "Registering DBus object on %s failed: %s", object_path, local_error->message); g_clear_error(&local_error); } if (instance) { g_hash_table_insert(objects, no_const_char_p(instance_name), GUINT_TO_POINTER(instance)); if (log_success) log_message(LOG_INFO, "Added DBus object for instance %s on path %s", instance_name, object_path); } g_free(object_path); return DBUS_SUCCESS; } static void dbus_create_object(vrrp_t *vrrp) { dbus_create_object_params(vrrp->iname, vrrp->ifp ? IF_NAME(VRRP_CONFIGURED_IFP(vrrp)) : dbus_no_interface_name, vrrp->vrid, vrrp->family, false); } static bool dbus_emit_signal(GDBusConnection *connection, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters) { GError *local_error = NULL; g_dbus_connection_emit_signal(connection, NULL, object_path, interface_name, signal_name, parameters, &local_error); if (local_error != NULL) { log_message(LOG_INFO, "Emitting DBus signal %s.%s on %s failed: %s", interface_name, signal_name, object_path, local_error->message); g_clear_error(&local_error); return false; } return true; } /* first function to be run when trying to own bus, * exports objects to the bus */ static void on_bus_acquired(GDBusConnection *connection, const gchar *name, __attribute__((unused)) gpointer user_data) { global_connection = connection; gchar *path; vrrp_t *vrrp; GError *local_error = NULL; guint vrrp_guint; log_message(LOG_INFO, "Acquired DBus bus %s", name); /* register VRRP object */ path = dbus_object_create_path_vrrp(); vrrp_guint = g_dbus_connection_register_object(connection, path, vrrp_introspection_data->interfaces[0], &interface_vtable, NULL, NULL, &local_error); g_hash_table_insert(objects, no_const_char_p("__Vrrp__"), GUINT_TO_POINTER(vrrp_guint)); if (local_error != NULL) { log_message(LOG_INFO, "Registering VRRP object on %s failed: %s", path, local_error->message); g_clear_error(&local_error); } g_free(path); /* for each available VRRP instance, register an object */ if (list_empty(&vrrp_data->vrrp)) return; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) dbus_create_object(vrrp); /* Send a signal to say we have started */ path = dbus_object_create_path_vrrp(); dbus_emit_signal(global_connection, path, DBUS_VRRP_INTERFACE, "VrrpStarted", NULL); g_free(path); /* Notify DBus of the state of our instances */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) dbus_send_state_signal(vrrp); } /* run if bus name is acquired successfully */ static void on_name_acquired(__attribute__((unused)) GDBusConnection *connection, const gchar *name, __attribute__((unused)) gpointer user_data) { log_message(LOG_INFO, "Acquired the name %s on the session bus", name); } /* run if bus name or connection are lost */ static void on_name_lost(GDBusConnection *connection, const gchar *name, __attribute__((unused)) gpointer user_data) { log_message(LOG_INFO, "Lost the name %s on the session bus", name); global_connection = connection; g_hash_table_foreach_remove(objects, remove_object, NULL); objects = NULL; global_connection = NULL; } /* This function must be called from the main thread since MALLOC isn't thread safe */ static const gchar* read_file(const gchar* filepath) { int fd; gchar *ret = NULL; struct stat statbuf; if ((fd = open(filepath, O_RDONLY)) == -1) { log_message(LOG_INFO, "Unable to open DBus file %s, errno %d (%m)", filepath, errno); return NULL; } if (fstat(fd, &statbuf)) log_message(LOG_INFO, "Unable to get DBus file size %s - %d (%m)", filepath, errno); else if (!(ret = MALLOC(statbuf.st_size + 1))) log_message(LOG_INFO, "Unable to malloc for Dbus file %s", filepath); else if (read(fd, ret, statbuf.st_size) != statbuf.st_size) { log_message(LOG_INFO, "Failed to read all of %s - %d (%m)", filepath, errno); FREE(ret); ret = NULL; } else ret[statbuf.st_size] = '\0'; close(fd); return ret; } static void * free_wait(void) { /* Ensure the thread that started this thread * is executing pthread_cond_wait() */ pthread_mutex_lock(&cond_mutex); dbus_startup_completed = true; pthread_cond_signal(&startup_cond); pthread_mutex_unlock(&cond_mutex); return NULL; } static void * dbus_main(void *param) { guint owner_id; const char *service_name; dbus_files_t *files = param; GError *error = NULL; objects = g_hash_table_new(g_str_hash, g_str_equal); /* DBus service org.keepalived.Vrrp1 exposes two interfaces, Vrrp and Instance. * Vrrp is implemented by a single VRRP object for general purposes, such as printing * data or signaling that the VRRP process has been stopped. * Instance is implemented by an Instance object for every VRRP Instance in vrrp_data. * It exposes instance specific methods and properties. */ #ifdef DBUS_NEED_G_TYPE_INIT g_type_init(); #endif /* read service interface data from xml files */ vrrp_introspection_data = g_dbus_node_info_new_for_xml(files->interface_file, &error); if (error != NULL) { log_message(LOG_INFO, "Parsing DBus interface %s from file %s failed: %s", DBUS_VRRP_INTERFACE, DBUS_VRRP_INTERFACE_FILE_PATH, error->message); g_clear_error(&error); return free_wait(); } vrrp_instance_introspection_data = g_dbus_node_info_new_for_xml(files->instance_interface_file, &error); if (error != NULL) { log_message(LOG_INFO, "Parsing DBus interface %s from file %s failed: %s", DBUS_VRRP_INSTANCE_INTERFACE, DBUS_VRRP_INSTANCE_INTERFACE_FILE_PATH, error->message); g_clear_error(&error); return free_wait(); } if (global_data->dbus_no_interface_name) dbus_no_interface_name = global_data->dbus_no_interface_name; service_name = global_data->dbus_service_name ? global_data->dbus_service_name : DBUS_SERVICE_NAME; owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, service_name, G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, NULL, /* user_data */ NULL); /* user_data_free_func */ loop = g_main_loop_new(NULL, FALSE); /* Notify main thread that we have completed initialising */ dbus_running = true; free_wait(); g_main_loop_run(loop); dbus_running = false; /* cleanup after loop terminates */ g_main_loop_unref(loop); loop = NULL; g_bus_unown_name(owner_id); global_connection = NULL; sem_post(&thread_end); pthread_exit(0); } /* The following functions are run in the context of the main vrrp thread */ /* send signal VrrpStatusChange * containing the new state of vrrp */ void dbus_send_state_signal(vrrp_t *vrrp) { gchar *object_path; GVariant *args; /* the interface will go through the initial state changes before * the main loop can be started and global_connection initialised */ if (global_connection == NULL) return; object_path = dbus_object_create_path_instance(vrrp->ifp ? IF_NAME(VRRP_CONFIGURED_IFP(vrrp)) : dbus_no_interface_name, vrrp->vrid, vrrp->family); args = g_variant_new("(u)", vrrp->state); dbus_emit_signal(global_connection, object_path, DBUS_VRRP_INSTANCE_INTERFACE, "VrrpStatusChange", args); g_free(object_path); } /* send signal VrrpRestarted */ static void dbus_send_reload_signal(void) { gchar *path; if (global_connection == NULL) return; path = dbus_object_create_path_vrrp(); dbus_emit_signal(global_connection, path, DBUS_VRRP_INTERFACE, "VrrpReloaded", NULL); g_free(path); } static gboolean dbus_unregister_object(const char *str) { gboolean ret = false; gpointer value = g_hash_table_lookup(objects, str); if (value) { ret = unregister_object(str, value, NULL); log_message(LOG_INFO, "Deleted DBus object for instance %s", str); } #ifdef DBUS_DEBUG else log_message(LOG_INFO, "DBus object not found for instance %s", str); #endif return ret; } void dbus_remove_object(const vrrp_t *vrrp) { dbus_unregister_object(vrrp->iname); } static void handle_dbus_msg(__attribute__((unused)) thread_ref_t thread) { dbus_queue_ent_t *ent; char recv_buf; vrrp_t *vrrp; #ifdef _WITH_DBUS_CREATE_INSTANCE_ gchar *name; #endif if (read(dbus_in_pipe[0], &recv_buf, 1) != 1) log_message(LOG_INFO, "Read from DBus thread to vrrp thread failed"); if ((ent = ent_ptr) != NULL) { ent->reply = DBUS_SUCCESS; if (ent->action == DBUS_PRINT_DATA) { log_message(LOG_INFO, "Printing VRRP data on DBus request"); vrrp_print_data(); } else if (ent->action == DBUS_PRINT_STATS) { log_message(LOG_INFO, "Printing VRRP stats on DBus request"); vrrp_print_stats(false); } else if (ent->action == DBUS_PRINT_STATS_CLEAR) { log_message(LOG_INFO, "Printing and clearing VRRP stats on DBus request"); vrrp_print_stats(true); } #ifdef _WITH_DBUS_CREATE_INSTANCE_ else if (ent->action == DBUS_CREATE_INSTANCE) { g_variant_get(ent->args, "(s)", &name); ent->reply = dbus_create_object_params(name, ent->ifname, ent->vrid, ent->family, true); } else if (ent->action == DBUS_DESTROY_INSTANCE) { g_variant_get(ent->args, "(s)", &name); if (!dbus_unregister_object(name)) ent->reply = DBUS_INSTANCE_NOT_FOUND; } #endif else if (ent->action == DBUS_SEND_GARP) { ent->reply = DBUS_INTERFACE_NOT_FOUND; vrrp = get_vrrp_instance(ent->ifname, ent->vrid, ent->family); if (vrrp) { log_message(LOG_INFO, "Sending garps on %s on DBus request", vrrp->iname); vrrp_send_link_update(vrrp, 1); ent->reply = DBUS_SUCCESS; } } else if (ent->action == DBUS_GET_NAME || ent->action == DBUS_GET_STATUS) { /* we look for the vrrp instance object that corresponds to our interface and group */ ent->reply = DBUS_INTERFACE_NOT_FOUND; vrrp = get_vrrp_instance(ent->ifname, ent->vrid, ent->family); if (vrrp) { /* the property_name argument is the property we want to Get */ if (ent->action == DBUS_GET_NAME) ent->args = g_variant_new("(s)", vrrp->iname); else if (ent->action == DBUS_GET_STATUS) ent->args = g_variant_new("(us)", vrrp->state, state_str(vrrp->state)); else ent->args = NULL; /* How did we get here? */ ent->reply = DBUS_SUCCESS; } } if (write(dbus_out_pipe[1], &recv_buf, 1) != 1) log_message(LOG_INFO, "Write from main thread to DBus thread failed"); } thread_add_read(master, handle_dbus_msg, NULL, dbus_in_pipe[0], TIMER_NEVER, 0); } void dbus_reload(const list_head_t *o, const list_head_t *n) { vrrp_t *vrrp_n, *vrrp_o; if (!dbus_running) return; list_for_each_entry(vrrp_n, n, e_list) { const char *n_name; bool match_found; n_name = vrrp_n->ifp ? VRRP_CONFIGURED_IFP(vrrp_n)->ifname : dbus_no_interface_name; /* Try and find an instance with same vrid/family/interface that existed before and now */ match_found = false; list_for_each_entry(vrrp_o, o, e_list) { if (vrrp_n->vrid == vrrp_o->vrid && vrrp_n->family == vrrp_o->family && // TODO - need to match on unicast_src if no interface !strcmp(n_name, VRRP_CONFIGURED_IFP(vrrp_o)->ifname)) { /* If the old instance exists in the new config, * then the dbus object will exist */ if (!strcmp(vrrp_n->iname, vrrp_o->iname)) { match_found = true; g_hash_table_replace(objects, no_const_char_p(vrrp_n->iname), g_hash_table_lookup(objects, vrrp_o->iname)); break; } else { gpointer instance; if ((instance = g_hash_table_lookup(objects, vrrp_o->iname))) { g_hash_table_remove(objects, vrrp_o->iname); g_hash_table_insert(objects, no_const_char_p(vrrp_n->iname), instance); } match_found = true; break; } #if 0 /* The following was in the original code, but I can't work out * its purpose. Leaving it here for now in case it is really needed. */ /* Check if the old instance name we found still exists * (but has a different vrid/family/interface) */ list_for_each_entry(vrrp_n3, n, e_list) { if (!strcmp(vrrp_o->iname, vrrp_n3->iname)) { match_found = true; break; } } #endif } } if (match_found) continue; dbus_create_object(vrrp_n); } /* Signal we have reloaded */ dbus_send_reload_signal(); /* We need to reinstate the read thread */ thread_add_read(master, handle_dbus_msg, NULL, dbus_in_pipe[0], TIMER_NEVER, 0); } static bool dbus_start_error(dbus_files_t *files) { if (files->interface_file) FREE_CONST(files->interface_file); if (files->instance_interface_file) FREE_CONST(files->instance_interface_file); if (dbus_in_pipe[0] != -1) { close(dbus_in_pipe[0]); close(dbus_in_pipe[1]); dbus_in_pipe[0] = -1; } if (dbus_out_pipe[0] != -1) { close(dbus_out_pipe[0]); close(dbus_out_pipe[1]); dbus_out_pipe[0] = -1; } return false; } bool dbus_start(void) { pthread_t dbus_thread; sigset_t sigset, cursigset; int flags; dbus_files_t files; if (dbus_running) return false; /* read service interface data from xml files */ if (!(files.interface_file = read_file(DBUS_VRRP_INTERFACE_FILE_PATH))) return false; if (!(files.instance_interface_file = read_file(DBUS_VRRP_INSTANCE_INTERFACE_FILE_PATH))) { FREE_CONST(files.interface_file); return false; } if (open_pipe(dbus_in_pipe)) { log_message(LOG_INFO, "Unable to create inbound dbus pipe - disabling DBus"); return dbus_start_error(&files); } if (open_pipe(dbus_out_pipe)) { log_message(LOG_INFO, "Unable to create outbound dbus pipe - disabling DBus"); return dbus_start_error(&files); } /* We don't want the main thread to block when using the pipes, * but the other side of the pipes should block. */ flags = fcntl(dbus_in_pipe[1], F_GETFL); if (flags == -1 || fcntl(dbus_in_pipe[1], F_SETFL, flags & ~O_NONBLOCK) == -1) log_message(LOG_INFO, "Unable to set dbus thread in_pipe blocking - (%d - %m)", errno); flags = fcntl(dbus_out_pipe[0], F_GETFL); if (flags == -1 || fcntl(dbus_out_pipe[0], F_SETFL, flags & ~O_NONBLOCK) == -1) log_message(LOG_INFO, "Unable to set dbus thread out_pipe blocking - (%d - %m)", errno); thread_add_read(master, handle_dbus_msg, NULL, dbus_in_pipe[0], TIMER_NEVER, 0); /* Initialise the thread termination semaphore */ sem_init(&thread_end, 0, 0); /* Block signals (all) we don't want the new thread to process */ sigfillset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, &cursigset); /* Make sure that the DBus thread is running before we return, or * we know it has failed. */ pthread_mutex_lock(&cond_mutex); /* Now create the dbus thread */ pthread_create(&dbus_thread, NULL, &dbus_main, &files); while (!dbus_startup_completed) pthread_cond_wait(&startup_cond, &cond_mutex); pthread_mutex_unlock(&cond_mutex); /* Reenable our signals */ pthread_sigmask(SIG_SETMASK, &cursigset, NULL); if (!dbus_running) { log_message(LOG_INFO, "Failed to initialise DBus"); return dbus_start_error(&files); } FREE_CONST(files.interface_file); FREE_CONST(files.instance_interface_file); return true; } void dbus_stop(void) { struct timespec thread_end_wait; int ret; gchar *path; if (!dbus_running) return; if (objects) { g_hash_table_foreach_remove(objects, remove_object, NULL); objects = NULL; } if (global_connection != NULL) { path = dbus_object_create_path_vrrp(); dbus_emit_signal(global_connection, path, DBUS_VRRP_INTERFACE, "VrrpStopped", NULL); g_free(path); } g_main_loop_quit(loop); g_dbus_node_info_unref(vrrp_introspection_data); g_dbus_node_info_unref(vrrp_instance_introspection_data); clock_gettime(CLOCK_REALTIME, &thread_end_wait); thread_end_wait.tv_sec += 1; while ((ret = sem_timedwait(&thread_end, &thread_end_wait)) == -1 && check_EINTR(errno)); if (ret == -1 ) { if (errno == ETIMEDOUT) log_message(LOG_INFO, "DBus thread termination timed out"); else log_message(LOG_INFO, "sem_timewait error %d", errno); } else { log_message(LOG_INFO, "Released DBus"); sem_destroy(&thread_end); } dbus_running = false; close(dbus_in_pipe[0]); close(dbus_in_pipe[1]); dbus_in_pipe[0] = -1; close(dbus_out_pipe[0]); close(dbus_out_pipe[1]); dbus_out_pipe[0] = -1; } #ifdef THREAD_DUMP void register_vrrp_dbus_addresses(void) { register_thread_address("handle_dbus_msg", handle_dbus_msg); } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_notify.c0000664000175000017500000002334014134601064015143 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: VRRP state transition notification scripts handling. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" /* system include */ #include #include #include /* local include */ #include "vrrp_notify.h" #include "vrrp_data.h" #ifdef _WITH_DBUS_ #include "vrrp_dbus.h" #endif #include "global_data.h" #include "notify.h" #include "logger.h" #if defined _WITH_SNMP_RFC_ || defined _WITH_SNMP_VRRP_ #include "vrrp_snmp.h" #endif #include "smtp.h" static notify_script_t* get_iscript(vrrp_t * vrrp) { if (!vrrp->notify_exec) return NULL; if (vrrp->state == VRRP_STATE_BACK) return vrrp->script_backup; if (vrrp->state == VRRP_STATE_MAST) return vrrp->script_master; if (vrrp->state == VRRP_STATE_FAULT) return vrrp->script_fault; if (vrrp->state == VRRP_STATE_STOP) return vrrp->script_stop; if (vrrp->state == VRRP_STATE_DELETED) return vrrp->script_deleted; return NULL; } static notify_script_t* get_gscript(vrrp_sgroup_t * vgroup, int state) { if (!vgroup->notify_exec) return NULL; if (state == VRRP_STATE_BACK) return vgroup->script_backup; if (state == VRRP_STATE_MAST) return vgroup->script_master; if (state == VRRP_STATE_FAULT) return vgroup->script_fault; if (state == VRRP_STATE_STOP) return vgroup->script_stop; return NULL; } static inline notify_script_t* get_igscript(vrrp_t *vrrp) { return vrrp->script; } static inline notify_script_t* get_ggscript(vrrp_sgroup_t * vgroup) { return vgroup->script; } static void notify_fifo(const char *name, int state_num, bool group, uint8_t priority) { const char *state = "{UNKNOWN}"; size_t size; char *line; const char *type; if (global_data->notify_fifo.fd == -1 && global_data->vrrp_notify_fifo.fd == -1) return; switch (state_num) { case VRRP_STATE_MAST: state = "MASTER"; break; case VRRP_STATE_BACK: state = "BACKUP"; break; case VRRP_STATE_FAULT: state = "FAULT"; break; case VRRP_STATE_STOP: state = "STOP"; break; case VRRP_STATE_DELETED: state = "DELETED"; break; case VRRP_EVENT_MASTER_RX_LOWER_PRI: state = "MASTER_RX_LOWER_PRI"; break; case VRRP_EVENT_MASTER_PRIORITY_CHANGE: state = "MASTER_PRIORITY"; break; case VRRP_EVENT_BACKUP_PRIORITY_CHANGE: state = "BACKUP_PRIORITY"; break; } type = group ? "GROUP" : "INSTANCE"; size = strlen(type) + strlen(state) + strlen(name) + 10; line = MALLOC(size); if (!line) return; snprintf(line, size, "%s \"%s\" %s %d\n", type, name, state, priority); if (global_data->notify_fifo.fd != -1) { if (write(global_data->notify_fifo.fd, line, strlen(line)) == -1) { /* empty */ } } if (global_data->vrrp_notify_fifo.fd != -1) { if (write(global_data->vrrp_notify_fifo.fd, line, strlen(line)) == -1) { /* empty */ } } FREE(line); } void notify_instance_fifo(const vrrp_t *vrrp) { notify_fifo(vrrp->iname, vrrp->state, false, vrrp->effective_priority); } void notify_group_fifo(const vrrp_sgroup_t *vgroup) { notify_fifo(vgroup->gname, vgroup->state, true, 0); } static void notify_script_exec(notify_script_t* script, const char *type, int state_num, const char* name, int prio) { char prio_buf[4]; /* * script {GROUP|INSTANCE} NAME {MASTER|BACKUP|FAULT|STOP|DELETED} PRIO * * Note that the prio will be indicated as zero for a group. * */ script->args[script->num_args] = type; script->args[script->num_args+1] = name; switch (state_num) { case VRRP_STATE_MAST : script->args[script->num_args+2] = "MASTER" ; break; case VRRP_STATE_BACK : script->args[script->num_args+2] = "BACKUP" ; break; case VRRP_STATE_FAULT : script->args[script->num_args+2] = "FAULT" ; break; case VRRP_STATE_STOP : script->args[script->num_args+2] = "STOP" ; break; case VRRP_STATE_DELETED: script->args[script->num_args+2] = "DELETED" ; break; default: script->args[script->num_args+2] = "{UNKNOWN}"; break; } snprintf(prio_buf, sizeof(prio_buf), "%d", prio); script->args[script->num_args+3] = prio_buf; script->num_args += 4; /* Launch the script */ if (state_num == VRRP_STATE_STOP) system_call_script(master, child_killed_thread, NULL, TIMER_HZ, script); else notify_exec(script); script->num_args -= 4; } /* SMTP alert notifier */ static void vrrp_smtp_notifier(vrrp_t * vrrp) { if (vrrp->smtp_alert && (!global_data->no_email_faults || vrrp->state != VRRP_STATE_FAULT) && vrrp->last_email_state != vrrp->state) { if (vrrp->state == VRRP_STATE_MAST) smtp_alert(SMTP_MSG_VRRP, vrrp, "Entering MASTER state", "=> VRRP Instance is now owning VRRP VIPs <="); else if (vrrp->state == VRRP_STATE_BACK) smtp_alert(SMTP_MSG_VRRP, vrrp, "Entering BACKUP state", "=> VRRP Instance is no longer owning VRRP VIPs <="); else if (vrrp->state == VRRP_STATE_FAULT) smtp_alert(SMTP_MSG_VRRP, vrrp, "Entering FAULT state", "=> VRRP Instance is no longer owning VRRP VIPs <="); else if (vrrp->state == VRRP_STATE_STOP) smtp_alert(SMTP_MSG_VRRP, vrrp, "Stopping", "=> VRRP Instance stopping <="); else if (vrrp->state == VRRP_STATE_DELETED) smtp_alert(SMTP_MSG_VRRP, vrrp, "Deleted", "=> VRRP Deleted at reload <="); else return; vrrp->last_email_state = vrrp->state; } } /* SMTP alert group notifier */ static void vrrp_sync_smtp_notifier(vrrp_sgroup_t *vgroup) { if (vgroup->smtp_alert && (!global_data->no_email_faults || vgroup->state != VRRP_STATE_FAULT) && vgroup->last_email_state != vgroup->state) { if (vgroup->state == VRRP_STATE_MAST) smtp_alert(SMTP_MSG_VGROUP, vgroup, "Entering MASTER state", "=> All VRRP group instances are now in MASTER state <="); else if (vgroup->state == VRRP_STATE_BACK) smtp_alert(SMTP_MSG_VGROUP, vgroup, "Entering BACKUP state", "=> All VRRP group instances are now in BACKUP state <="); else if (vgroup->state == VRRP_STATE_FAULT) smtp_alert(SMTP_MSG_VGROUP, vgroup, "Entering FAULT state", "=> All VRRP group instances are now in FAULT state <="); else if (vgroup->state == VRRP_STATE_STOP) smtp_alert(SMTP_MSG_VGROUP, vgroup, "Stopping", "=> All VRRP group instances are now stopping <="); else return; vgroup->last_email_state = vgroup->state; } } void send_event_notify(vrrp_t *vrrp, int event) { notify_script_t *script = vrrp->script_master_rx_lower_pri; /* Launch the notify_* script */ if (script) notify_exec(script); notify_fifo(vrrp->iname, event, false, vrrp->effective_priority); } void send_instance_notifies(vrrp_t *vrrp) { notify_script_t *script = get_iscript(vrrp); notify_script_t *gscript = get_igscript(vrrp); if (vrrp->notifies_sent && vrrp->sync && vrrp->state == vrrp->sync->state) { /* We are already in the required state due to our sync group, * so don't send further notifies. */ return; } /* The old way to notify an instance being deleted was to send FAULT, * and that is maintained as the default for backward compatibility. */ if (!vrrp->notify_deleted && vrrp->state == VRRP_STATE_DELETED) vrrp->state = VRRP_STATE_FAULT; vrrp->notifies_sent = true; /* Launch the notify_* script */ if (script) { if (vrrp->state == VRRP_STATE_STOP) system_call_script(master, child_killed_thread, NULL, TIMER_HZ, script); else notify_exec(script); } /* Launch the generic notify script */ if (gscript) notify_script_exec(gscript, "INSTANCE", vrrp->state, vrrp->iname, vrrp->effective_priority); notify_instance_fifo(vrrp); #ifdef _WITH_DBUS_ if (global_data->enable_dbus) dbus_send_state_signal(vrrp); // send signal to all subscribers #endif #ifdef _WITH_SNMP_VRRP_ vrrp_snmp_instance_trap(vrrp); #endif if (vrrp->state == VRRP_STATE_MAST) { #ifdef _WITH_SNMP_RFCV2_ vrrp_rfcv2_snmp_new_master_trap(vrrp); #endif #ifdef _WITH_SNMP_RFCV3_ vrrp_rfcv3_snmp_new_master_notify(vrrp); #endif } vrrp_smtp_notifier(vrrp); } void send_group_notifies(vrrp_sgroup_t *vgroup) { notify_script_t *script = get_gscript(vgroup, vgroup->state); notify_script_t *gscript = get_ggscript(vgroup); /* Launch the notify_* script */ if (script) notify_exec(script); /* Launch the generic notify script */ if (gscript) notify_script_exec(gscript, "GROUP", vgroup->state, vgroup->gname, 0); notify_group_fifo(vgroup); #ifdef _WITH_SNMP_VRRP_ vrrp_snmp_group_trap(vgroup); #endif vrrp_sync_smtp_notifier(vgroup); } void send_instance_priority_notifies(vrrp_t *vrrp) { notify_fifo(vrrp->iname, vrrp->state == VRRP_STATE_MAST ? VRRP_EVENT_MASTER_PRIORITY_CHANGE : VRRP_EVENT_BACKUP_PRIORITY_CHANGE, false, vrrp->effective_priority); } /* handle terminate state */ void notify_shutdown(void) { vrrp_t *vrrp; vrrp_sgroup_t *vgroup; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { vrrp->state = VRRP_STATE_STOP; send_instance_notifies(vrrp); } list_for_each_entry(vgroup, &vrrp_data->vrrp_sync_group, e_list) { vgroup->state = VRRP_STATE_STOP; send_group_notifies(vgroup); } } keepalived-2.3.3/keepalived/vrrp/vrrp_vmac_nm.c0000664000175000017500000000715214303340503015252 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: NETLINK VMAC NetworkManager manipulation. * * Author: Quentin Armitage, * * 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) 2022-2022 Alexandre Cassen, */ #include "config.h" /* global include */ #include #include #include #include /* local include */ #include "vrrp_vmac_nm.h" #include "logger.h" #if NM_CHECK_VERSION(1,22,0) static const char * nmc_error_get_simple_message(GError *error) { /* Return a clear message instead of the obscure D-Bus policy error */ if (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED)) return "access denied"; if (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) return "NetworkManager is not running"; return error->message; } static void _device_cb(GObject *object, GAsyncResult *result, gpointer user_data) { GMainLoop *loop = user_data; GError *error = NULL; if (!nm_client_dbus_set_property_finish(NM_CLIENT(object), result, &error)) { g_dbus_error_strip_remote_error(error); log_message(LOG_INFO, "Error: failed to set device managed status: %s", nmc_error_get_simple_message(error)); g_error_free(error); } /* Tell the mainloop we're done and we can quit now */ g_main_loop_quit(loop); } #endif /* Older versions of NetworkManager (certainly version 1.0.6, but resolved by 1.18) * set macvlans, when created, as manager by NetworkManager. This caused problems * when the underlying interface went down, since NM would then down the macvlan * interface and when the underlying interface recovered, the macvlan interface * would remain down, and the VRRP instance would remain in FAULT state. * This code, after a macvlan is created, sets the macvlan to be unmanaged by * NetworkManager. */ void set_vmac_unmanaged_nm(const char *vmac_name) { NMClient *client; NMDevice *device; GError *error = NULL; GMainLoop *loop; static bool logged_nm_version = false; #if NM_CHECK_VERSION(1,22,0) loop = g_main_loop_new(NULL, FALSE); #endif client = nm_client_new(NULL, &error); if (!client) { g_message("Error: Could not connect to NetworkManager: %s.", error->message); g_error_free(error); return; } if (!logged_nm_version) { logged_nm_version = true; log_message(LOG_INFO, "NetworkManager version: %s", nm_client_get_version (client)); } device = nm_client_get_device_by_iface(client, vmac_name); log_message(LOG_INFO, "Setting %s managed off on %s", vmac_name, nm_object_get_path((NMObject *)device)); #if NM_CHECK_VERSION(1,22,0) nm_client_dbus_set_property(client, nm_object_get_path((NMObject *)device), NM_DBUS_INTERFACE_DEVICE, "Managed", g_variant_new_boolean(false), -1, NULL, _device_cb, loop); g_main_loop_run(loop); #else nm_device_set_managed(device, 0); #endif g_object_unref(client); } keepalived-2.3.3/keepalived/vrrp/vrrp_vmac.c0000664000175000017500000006460414570440507014600 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: NETLINK VMAC address manipulation. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" /* global include */ #ifdef _HAVE_LINUX_IF_ETHER_H_COLLISION_ #include #endif #include #include /* local include */ #include "vrrp_vmac.h" #include "keepalived_netlink.h" #include "logger.h" #include "bitops.h" #include "utils.h" #include "vrrp_if_config.h" #include "vrrp_ipaddress.h" #include "vrrp_firewall.h" #include "global_data.h" #ifdef _HAVE_LIBNM_ #include "vrrp_vmac_nm.h" #endif const char * const macvlan_ll_kind = "macvlan"; #ifdef _HAVE_VRRP_IPVLAN_ const char * const ipvlan_ll_kind = "ipvlan"; #endif const u_char ll_addr[ETH_ALEN] = {0x00, 0x00, 0x5e, 0x00, 0x01, 0x00}; static void make_link_local_address(struct in6_addr* l3_addr, const u_char* if_ll_addr) { l3_addr->s6_addr[0] = 0xfe; l3_addr->s6_addr[1] = 0x80; l3_addr->s6_addr16[1] = 0; l3_addr->s6_addr32[1] = 0; l3_addr->s6_addr[8] = if_ll_addr[0] ^ 0x02; l3_addr->s6_addr[9] = if_ll_addr[1]; l3_addr->s6_addr[10] = if_ll_addr[2]; l3_addr->s6_addr[11] = 0xff; l3_addr->s6_addr[12] = 0xfe; l3_addr->s6_addr[13] = if_ll_addr[3]; l3_addr->s6_addr[14] = if_ll_addr[4]; l3_addr->s6_addr[15] = if_ll_addr[5]; } bool add_link_local_address(interface_t *ifp, struct in6_addr* sin6_addr) { ip_address_t ipaddress; memset(&ipaddress, 0, sizeof(ipaddress)); /* Delete the old address */ ipaddress.ifp = ifp; ipaddress.u.sin6_addr = *sin6_addr; ipaddress.ifa.ifa_family = AF_INET6; ipaddress.ifa.ifa_prefixlen = 64; ipaddress.ifa.ifa_index = ifp->ifindex; if (netlink_ipaddress(&ipaddress, IPADDRESS_ADD) != 1) { log_message(LOG_INFO, "Adding link-local address to vmac failed"); CLEAR_IP6_ADDR(&ifp->sin6_addr); return false; } /* Save the new address */ ifp->sin6_addr = ipaddress.u.sin6_addr; return true; } bool del_link_local_address(interface_t *ifp) { ip_address_t ipaddress; memset(&ipaddress, 0, sizeof(ipaddress)); /* Delete the old address */ ipaddress.ifp = ifp; ipaddress.u.sin6_addr = ifp->sin6_addr; ipaddress.ifa.ifa_family = AF_INET6; ipaddress.ifa.ifa_prefixlen = 64; ipaddress.ifa.ifa_index = ifp->ifindex; if (netlink_ipaddress(&ipaddress, IPADDRESS_DEL) != 1) { log_message(LOG_INFO, "Deleting link-local address from vmac failed"); return false; } CLEAR_IP6_ADDR(&ifp->sin6_addr); return true; } static bool change_link_local_address(interface_t *ifp, struct in6_addr *old_addr, struct in6_addr *new_addr) { ip_address_t ipaddress; /* There is no point in replacing the address with the same address */ if (inaddr_equal(AF_INET6, old_addr, new_addr)) return true; memset(&ipaddress, 0, sizeof(ipaddress)); /* Delete the old address */ ipaddress.ifp = ifp; ipaddress.u.sin6_addr = *old_addr; ipaddress.ifa.ifa_family = AF_INET6; ipaddress.ifa.ifa_prefixlen = 64; ipaddress.ifa.ifa_index = ifp->ifindex; if (netlink_ipaddress(&ipaddress, IPADDRESS_DEL) != 1) log_message(LOG_INFO, "Deleting link-local address from vmac failed"); else CLEAR_IP6_ADDR(&ifp->sin6_addr); ipaddress.u.sin6_addr = *new_addr; if (netlink_ipaddress(&ipaddress, IPADDRESS_ADD) != 1) { log_message(LOG_INFO, "Adding link-local address to vmac failed"); CLEAR_IP6_ADDR(&ifp->sin6_addr); return false; } return true; } bool replace_link_local_address(interface_t *ifp) { struct in6_addr ipaddress_new; /* Create a new address */ make_link_local_address(&ipaddress_new, ifp->base_ifp->hw_addr); if (!change_link_local_address(ifp, &ifp->sin6_addr, &ipaddress_new)) return false; /* Save the new address */ ifp->sin6_addr = ipaddress_new; return true; } bool reset_link_local_address(struct in6_addr *old_addr, vrrp_t *vrrp) { return change_link_local_address(vrrp->ifp, old_addr, &PTR_CAST(struct sockaddr_in6, &vrrp->saddr)->sin6_addr); } #if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE void remove_vmac_auto_gen_addr(interface_t *ifp, struct in6_addr *addr) { struct in6_addr auto_addr; ip_address_t ipaddress; make_link_local_address(&auto_addr, ifp->hw_addr); if (!inaddr_equal(AF_INET6, &auto_addr, addr)) return; /* Delete the new address */ memset(&ipaddress, 0, sizeof(ipaddress)); ipaddress.ifp = ifp; ipaddress.u.sin6_addr = *addr; ipaddress.ifa.ifa_family = AF_INET6; ipaddress.ifa.ifa_prefixlen = 64; ipaddress.ifa.ifa_index = ifp->ifindex; if (netlink_ipaddress(&ipaddress, IPADDRESS_DEL) != 1) log_message(LOG_INFO, "Deleting auto generated link-local address from vmac failed"); } #endif static int netlink_link_up(vrrp_t *vrrp) { int status = 1; struct { struct nlmsghdr n; struct ifinfomsg ifi; } req; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_UNSPEC; req.ifi.ifi_index = (int)IF_INDEX(vrrp->ifp); req.ifi.ifi_change |= IFF_UP; req.ifi.ifi_flags |= IFF_UP; if (netlink_talk(&nl_cmd, &req.n) < 0) status = -1; return status; } static void netlink_link_group(interface_t *base_ifp) { struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[256]; } req = { .buf[0] = 0 }; req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_UNSPEC; req.ifi.ifi_index = (int)IF_INDEX(base_ifp); addattr32(&req.n, sizeof(req), IFLA_GROUP, base_ifp->group); netlink_talk(&nl_cmd, &req.n); } bool set_link_local_address(const vrrp_t *vrrp) { /* Add link-local address. If a source address has been specified, use it, * else use link-local address from underlying interface to vmac if there is one, * otherwise construct a link-local address based on underlying interface's * MAC address. * This is so that VRRP advertisements will be sent from a non-VIP address, but * using the VRRP MAC address */ struct in6_addr addr; if (vrrp->saddr.ss_family == AF_INET6) addr = PTR_CAST_CONST(struct sockaddr_in6, &vrrp->saddr)->sin6_addr; else if (!IN6_IS_ADDR_UNSPECIFIED(&vrrp->configured_ifp->sin6_addr)) addr = vrrp->configured_ifp->sin6_addr; else make_link_local_address(&addr, vrrp->configured_ifp->base_ifp->hw_addr); return add_link_local_address(vrrp->ifp, &addr); } bool netlink_link_add_vmac(vrrp_t *vrrp, const interface_t *old_interface) { struct rtattr *linkinfo; struct rtattr *data; interface_t *ifp; bool create_interface = true; struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[256]; } req; u_char if_ll_addr[ETH_ALEN]; bool update_interface = false; bool ret = true; if (!vrrp->ifp || __test_bit(VRRP_VMAC_UP_BIT, &vrrp->flags) || !vrrp->vrid) return false; if (__test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags)) memcpy(if_ll_addr, vrrp->ll_addr, sizeof(vrrp->ll_addr)); else { memcpy(if_ll_addr, ll_addr, ETH_ALEN - 2); if (vrrp->family == AF_INET6) if_ll_addr[ETH_ALEN-2] = 0x02; else if_ll_addr[ETH_ALEN-2] = 0x01; if_ll_addr[ETH_ALEN-1] = vrrp->vrid; } memset(&req, 0, sizeof (req)); /* * Check to see if this vmac interface was created * by a previous instance. */ ifp = if_get_by_ifname(vrrp->vmac_ifname, IF_CREATE_ALWAYS); if (ifp->ifindex) { /* Check to see whether this interface has wrong mac ? * The parser checks the interface is a private mode macvlan. */ if (ifp->base_ifp->ifindex != vrrp->configured_ifp->ifindex || old_interface) { /* Be safe here - we don't want to remove a physical interface. * vrrp_vmac_handler() should have already ensured it is a macvlan */ if (ifp->if_type == IF_TYPE_MACVLAN) { /* We have found a VIF but the vmac or type do not match */ log_message(LOG_INFO, "(%s) Removing old VMAC interface %s due to conflicting " "interface" , vrrp->iname, vrrp->vmac_ifname); /* Request that NETLINK remove the VIF interface first */ memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_DELLINK; req.ifi.ifi_family = AF_INET; req.ifi.ifi_index = (int)IF_INDEX(ifp); if (netlink_talk(&nl_cmd, &req.n) < 0) { log_message(LOG_INFO, "(%s) Error removing VMAC interface %s" , vrrp->iname, vrrp->vmac_ifname); return false; } kernel_netlink_poll(); /* Update our local info */ } else { log_message(LOG_INFO, "VMAC %s conflicts with existing interface", vrrp->vmac_ifname); return false; } } else if (memcmp((const void *)ifp->hw_addr, (const void *)if_ll_addr, ETH_ALEN) != 0 || ifp->vmac_type != MACVLAN_MODE_PRIVATE) { log_message(LOG_INFO, "(%s) Update old VMAC interface %s due to wrong MAC/mode " , vrrp->iname, vrrp->vmac_ifname); update_interface = true; } else create_interface = false; } ifp->is_ours = true; if (create_interface && vrrp->configured_ifp->base_ifp->ifindex) { /* Request that NETLINK create the VIF interface */ req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; if (!update_interface) req.n.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; req.n.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_UNSPEC; if (update_interface) req.ifi.ifi_index = (int)IF_INDEX(ifp); /* macvlan settings */ linkinfo = PTR_CAST(struct rtattr, NLMSG_TAIL(&req.n)); addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, (const void *)macvlan_ll_kind, strlen(macvlan_ll_kind)); data = PTR_CAST(struct rtattr, NLMSG_TAIL(&req.n)); addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); /* * In private mode, macvlan will receive frames with same MAC addr * as configured on the interface. */ addattr32(&req.n, sizeof(req), IFLA_MACVLAN_MODE, MACVLAN_MODE_PRIVATE); data->rta_len = (unsigned short)((char *)NLMSG_TAIL(&req.n) - (char *)data); /* coverity[overrun-local] */ linkinfo->rta_len = (unsigned short)((char *)NLMSG_TAIL(&req.n) - (char *)linkinfo); if (!update_interface) { /* Note: if the underlying interface is a macvlan, then the kernel will configure the * interface on the underlying interface of the macvlan */ addattr32(&req.n, sizeof(req), IFLA_LINK, vrrp->configured_ifp->ifindex); addattr_l(&req.n, sizeof(req), IFLA_IFNAME, vrrp->vmac_ifname, strlen(vrrp->vmac_ifname)); } /* * Copy the group from the base interface to allow firewall rules * (iptables devgroup or nftables iifgroup, oifgroup) to continue * working regardless of the use_vmac setting. */ addattr32(&req.n, sizeof(req), IFLA_GROUP, __test_bit(VRRP_VMAC_GROUP, &vrrp->flags) ? vrrp->vmac_group : vrrp->configured_ifp->base_ifp->group); addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, if_ll_addr, ETH_ALEN); #ifdef _HAVE_VRF_ /* If the underlying interface is enslaved to a VRF master, then this * interface should be as well. */ if (vrrp->configured_ifp->vrf_master_ifp || update_interface) addattr32(&req.n, sizeof(req), IFLA_MASTER, vrrp->configured_ifp->vrf_master_ifp ? vrrp->configured_ifp->vrf_master_ifp->ifindex : 0); #endif if (netlink_talk(&nl_cmd, &req.n) < 0) { log_message(LOG_INFO, "(%s): Unable to create VMAC interface %s" , vrrp->iname, vrrp->vmac_ifname); return false; } if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s): Success %sating VMAC interface %s" , vrrp->iname, update_interface ? "upd" : "cre", vrrp->vmac_ifname); /* * Update interface queue and vrrp instance interface binding. */ netlink_interface_lookup(vrrp->vmac_ifname); if (!ifp->ifindex) return false; if (!ifp->base_ifp && IS_MAC_IP_VLAN(vrrp->configured_ifp) && vrrp->configured_ifp == vrrp->configured_ifp->base_ifp) { /* If the base interface is a MACVLAN/IPVLAN that has been moved into a * different network namespace from its parent, we can't find the parent */ ifp->base_ifp = ifp; } /* If we do anything that might cause the interface state to change, we must * read the reflected netlink messages to ensure that the link status doesn't * get updated by out of date queued messages */ kernel_netlink_poll(); } #ifdef _HAVE_LIBNM_ /* Set the interface not managed by NetworkManager */ set_vmac_unmanaged_nm(vrrp->vmac_ifname); #endif ifp->vmac_type = MACVLAN_MODE_PRIVATE; if (!ifp->ifindex) return false; if (create_interface) { /* Set the necessary kernel parameters to make macvlans work for us */ set_interface_parameters(ifp, ifp->base_ifp, vrrp->family); } #ifdef _WITH_FIREWALL_ if (vrrp->family == AF_INET6 || !global_data->disable_local_igmp) firewall_add_vmac(vrrp, old_interface); #endif /* We don't want IPv6 running on the interface unless we have some IPv6 * eVIPs, so disable it if not needed */ // This isn't right if the eVIPs are not on the VMAC if (vrrp->family == AF_INET && !__test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags)) link_set_ipv6(ifp, false); else link_set_ipv6(ifp, true); /* We don't want a link-local address auto assigned - see RFC5798 paragraph 7.4. * If we have a sufficiently recent kernel, we can stop a link local address * based on the MAC address being automatically assigned. If not, then we have * to delete the generated address after bringing the interface up (see below). */ #if HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE /* This can't be part of create/update i/f msg since the kernel * doesn't process IFLA_AF_SPEC when links are created. * We also up the interface here. */ memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_UNSPEC; req.ifi.ifi_index = (int)vrrp->ifp->ifindex; struct rtattr* spec; spec = PTR_CAST(struct rtattr, NLMSG_TAIL(&req.n)); addattr_l(&req.n, sizeof(req), IFLA_AF_SPEC, NULL,0); data = PTR_CAST(struct rtattr, NLMSG_TAIL(&req.n)); addattr_l(&req.n, sizeof(req), AF_INET6, NULL,0); addattr8(&req.n, sizeof(req), IFLA_INET6_ADDR_GEN_MODE, IN6_ADDR_GEN_MODE_NONE); /* coverity[overrun-local] */ data->rta_len = (unsigned short)((char *)NLMSG_TAIL(&req.n) - (char *)data); spec->rta_len = (unsigned short)((char *)NLMSG_TAIL(&req.n) - (char *)spec); if (netlink_talk(&nl_cmd, &req.n) < 0) log_message(LOG_INFO, "(%s) Error setting ADDR_GEN_MODE to NONE on %s", vrrp->iname, vrrp->ifp->ifname); #endif /* We cannot include the link up setting with the ADDR_GEN_MODE message above * since the link is set up and an EUI64 address added to the interface before * the ADDR_GEN_MODE setting is changed. */ netlink_link_up(vrrp); /* Mark it as UP ! */ __set_bit(VRRP_VMAC_UP_BIT, &vrrp->flags); if (vrrp->family == AF_INET6 && !__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags)) { if (!set_link_local_address(vrrp) && create_interface) { log_message(LOG_INFO, "(%s) adding link-local address to %s failed", vrrp->iname, vrrp->ifp->ifname); ret = false; } } /* If the base interface does not implement IFF_UNICAST_FLT, for example * it is a bridge interface, no netlink notification is sent by the kernel * when promiscuity is set on the base interface. * The promiscuous state of the base interface is correct in the kernel * but it is in incorrect in processes that listen to the interface netlink * messages due to the missing netlink message. * * Force a notification by re-setting IFLA_GROUP for the base interface. * NOTE: there is a window here where the group may have been changed by * some other process but we have not received the netlink message yet. */ if (create_interface && vrrp->configured_ifp->base_ifp->ifindex && __test_bit(VRRP_VMAC_NETLINK_NOTIFY, &vrrp->flags)) netlink_link_group(vrrp->configured_ifp->base_ifp); #if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE if (vrrp->family == AF_INET6 || __test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags)) { /* Delete the automatically created link-local address based on the * MAC address if we weren't able to configure the interface not to * create the address (see above). * This isn't ideal, since the invalid address will exist momentarily, * but is there any better way to do it? probably not otherwise * ADDR_GEN_MODE wouldn't have been added to the kernel. */ ip_address_t ipaddress; memset(&ipaddress, 0, sizeof(ipaddress)); ipaddress.u.sin6_addr = ifp->base_ifp->sin6_addr; make_link_local_address(&ipaddress.u.sin6_addr, if_ll_addr); ipaddress.ifa.ifa_family = AF_INET6; ipaddress.ifa.ifa_prefixlen = 64; ipaddress.ifa.ifa_index = vrrp->ifp->ifindex; ipaddress.ifp = vrrp->ifp; if (netlink_ipaddress(&ipaddress, IPADDRESS_DEL) != 1 && create_interface) log_message(LOG_INFO, "Deleting auto link-local address from vmac failed"); } #endif /* If we are adding a large number of interfaces, the netlink socket * may run out of buffers if we don't receive the netlink messages * as we progress */ kernel_netlink_poll(); return ret; } #ifdef _INCLUDE_UNUSED_CODE_ typedef struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[256]; } req_t; static void dump_bufn(const char *msg, req_t *req) { size_t i; char buf[3 * req->n.nlmsg_len + 3]; char *ptr = buf; log_message(LOG_INFO, "%s: message length is %d\n", msg, req->n.nlmsg_len); for (i = 0; i < req->n.nlmsg_len; i++) ptr += snprintf(ptr, buf + sizeof buf - ptr, "%2.2x ", PTR_CAST(unsigned char, &req->n)[i]); log_message(LOG_INFO, "%s", buf); } #endif #ifdef _HAVE_VRRP_IPVLAN_ bool netlink_link_add_ipvlan(vrrp_t *vrrp) { struct rtattr *linkinfo; struct rtattr *data; interface_t *ifp; bool create_interface = true; struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[256]; } req; if (!vrrp->ifp || __test_bit(VRRP_VMAC_UP_BIT, &vrrp->flags) || !vrrp->vrid) return false; /* * Check to see if this ipvlan interface was created * by a previous instance. */ ifp = if_get_by_ifname(vrrp->vmac_ifname, IF_CREATE_ALWAYS); if (ifp->ifindex) create_interface = false; ifp->is_ours = true; if (create_interface && vrrp->configured_ifp->base_ifp->ifindex) { memset(&req, 0, sizeof (req)); /* Request that NETLINK create the VIF interface */ req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; req.n.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_UNSPEC; req.ifi.ifi_change |= IFF_UP; req.ifi.ifi_flags |= IFF_UP; /* ipvlan settings */ /* Note: if the underlying interface is a ipvlan, then the kernel will configure the * interface only the underlying interface of the ipvlan. * We copy the group from the base interface to allow firewall rules * (iptables devgroup or nftables iifgroup, oifgroup) to continue * working regardless of the use_vmac setting. */ addattr32(&req.n, sizeof(req), IFLA_LINK, vrrp->configured_ifp->ifindex); addattr_l(&req.n, sizeof(req), IFLA_IFNAME, vrrp->vmac_ifname, strlen(vrrp->vmac_ifname)); addattr32(&req.n, sizeof(req), IFLA_GROUP, __test_bit(VRRP_VMAC_GROUP, &vrrp->flags) ? vrrp->vmac_group : vrrp->configured_ifp->base_ifp->group); linkinfo = PTR_CAST(struct rtattr, NLMSG_TAIL(&req.n)); addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, (const void *)ipvlan_ll_kind, strlen(ipvlan_ll_kind)); data = PTR_CAST(struct rtattr, NLMSG_TAIL(&req.n)); addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); /* * In l2 mode, ipvlan will receive frames. */ addattr16(&req.n, sizeof(req), IFLA_IPVLAN_MODE, IPVLAN_MODE_L2); #if HAVE_DECL_IFLA_IPVLAN_FLAGS addattr16(&req.n, sizeof(req), IFLA_IPVLAN_FLAGS, vrrp->ipvlan_type); #endif /* coverity[overrun-local] */ data->rta_len = (unsigned short)((char *)NLMSG_TAIL(&req.n) - (char *)data); linkinfo->rta_len = (unsigned short)((char *)NLMSG_TAIL(&req.n) - (char *)linkinfo); #ifdef _HAVE_VRF_ /* If the underlying interface is enslaved to a VRF master, then this * interface should be as well. */ if (vrrp->configured_ifp->vrf_master_ifp) addattr32(&req.n, sizeof(req), IFLA_MASTER, vrrp->configured_ifp->vrf_master_ifp->ifindex); #endif if (netlink_talk(&nl_cmd, &req.n) < 0) { log_message(LOG_INFO, "(%s): Unable to create ipvlan interface %s" , vrrp->iname, vrrp->vmac_ifname); return false; } if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s): Success creating ipvlan interface %s" , vrrp->iname, vrrp->vmac_ifname); /* * Update interface queue and vrrp instance interface binding. */ netlink_interface_lookup(vrrp->vmac_ifname); if (!ifp->ifindex) return false; if (!ifp->base_ifp && (vrrp->configured_ifp->if_type == IF_TYPE_MACVLAN || vrrp->configured_ifp->if_type == IF_TYPE_IPVLAN) && vrrp->configured_ifp == vrrp->configured_ifp->base_ifp) { /* If the base interface is a MACVLAN that has been moved into a * different network namespace from its parent, we can't find the parent */ ifp->base_ifp = ifp; } __set_bit(VRRP_VMAC_UP_BIT, &vrrp->flags); } else if (!(ifp->ifi_flags & IFF_UP)) { /* bring it UP ! */ netlink_link_up(vrrp); __set_bit(VRRP_VMAC_UP_BIT, &vrrp->flags); } /* If we do anything that might cause the interface state to change, we must * read the reflected netlink messages to ensure that the link status doesn't * get updated by out of date queued messages */ kernel_netlink_poll(); ifp->vmac_type = IPVLAN_MODE_L2; if (!ifp->ifindex) return false; /* We don't want IPv6 running on the interface unless we have some IPv6 * eVIPs, so disable it if not needed */ if (vrrp->family == AF_INET && !__test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags)) link_set_ipv6(ifp, false); else link_set_ipv6(ifp, true); if (vrrp->ipvlan_addr) { if (netlink_ipaddress(vrrp->ipvlan_addr, IPADDRESS_ADD) != 1) log_message(LOG_INFO, "%s: Failed to add interface address to %s", vrrp->iname, ifp->ifname); else { if (vrrp->ipvlan_addr->ifa.ifa_family == AF_INET) ifp->sin_addr = vrrp->ipvlan_addr->u.sin.sin_addr; else ifp->sin6_addr = vrrp->ipvlan_addr->u.sin6_addr; } } return true; } #endif void netlink_link_del_vmac(vrrp_t *vrrp) { struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[256]; } req; if (!vrrp->ifp) return; /* Don't delete the VMAC if it isn't an interface we created */ if (!vrrp->ifp->is_ours) { log_message(LOG_INFO, "BUG - Attempt to remove VMAC interface %s which we didn't create", vrrp->ifp->ifname); return; } /* Reset arp_ignore and arp_filter on the base interface if necessary */ if (vrrp->family == AF_INET) { if (vrrp->ifp->base_ifp) reset_interface_parameters(vrrp->ifp->base_ifp); else log_message(LOG_INFO, "Unable to find base interface for vrrp instance %s", vrrp->iname); } /* If the interface doesn't exist, don't try to delete it */ if (!vrrp->ifp->ifindex) return; memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_DELLINK; req.ifi.ifi_family = AF_INET; req.ifi.ifi_index = (int)vrrp->ifp->ifindex; if (netlink_talk(&nl_cmd, &req.n) < 0) { log_message(LOG_INFO, "(%s) Error removing VMAC interface %s" , vrrp->iname, vrrp->vmac_ifname); return; } if (__test_bit(VRRP_VMAC_NETLINK_NOTIFY, &vrrp->flags)) { /* Force a netlink RTM_NEWLINK message for the base interface * since promiscuity may have been decremented. */ netlink_link_group(vrrp->configured_ifp->base_ifp); } #ifdef _WITH_FIREWALL_ // Why do we need this test? // PROBLEM !!! We have deleted the link, but firewall_remove_vmac uses the ifindex. if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && (vrrp->family == AF_INET6 || !global_data->disable_local_igmp)) firewall_remove_vmac(vrrp); #endif if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Success removing VMAC interface %s" , vrrp->iname, vrrp->vmac_ifname); /* Ensure we don't try and recreate the interface */ vrrp->ifp->deleting = true; kernel_netlink_poll(); vrrp->ifp->deleting = false; vrrp->ifp->is_ours = false; return; } #ifdef _HAVE_VRF_ static void netlink_update_vrf(vrrp_t *vrrp) { int ifindex = 0; struct { struct nlmsghdr n; struct ifinfomsg ifi; char buf[256]; } req; if (!vrrp->ifp) return; /* Don't update the VMAC if it isn't an interface we created */ if (!vrrp->ifp->is_ours) { log_message(LOG_INFO, "BUG - Attempt to update VRF on VMAC interface %s which we didn't create", vrrp->ifp->ifname); return; } memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct ifinfomsg)); req.n.nlmsg_flags = NLM_F_REQUEST; req.n.nlmsg_type = RTM_NEWLINK; req.ifi.ifi_family = AF_INET; req.ifi.ifi_index = (int)vrrp->ifp->ifindex; if (vrrp->ifp->vrf_master_ifp) ifindex = vrrp->ifp->vrf_master_ifp->ifindex; addattr32(&req.n, sizeof(req), IFLA_MASTER, ifindex); if (netlink_talk(&nl_cmd, &req.n) < 0) { log_message(LOG_INFO, "vmac: Error changing VRF of VMAC interface %s for vrrp_instance %s!!!", vrrp->ifp->ifname, vrrp->iname); return; } if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "vmac: Success changing VRF of VMAC interface %s for vrrp_instance %s", vrrp->ifp->ifname, vrrp->iname); kernel_netlink_poll(); return; } void update_vmac_vrfs(interface_t *ifp) { tracking_obj_t *top; vrrp_t *vrrp; list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; /* We only need to look for vmacs we created that * are configured on the interface which has changed * VRF */ if (vrrp->configured_ifp != ifp || !vrrp->ifp->is_ours) continue; vrrp->ifp->vrf_master_ifp = ifp->vrf_master_ifp; if (vrrp->ifp->ifindex) netlink_update_vrf(vrrp); } } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_ip_rule_route_parser.c0000664000175000017500000001504714227044032020070 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: iprule and iproute parser * * Author: Chris Riley, * * 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) 2015 Chris Riley, * Copyright (C) 2016-2017 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include #include #include #include #include "logger.h" #include "vrrp_ip_rule_route_parser.h" #include "rttables.h" #if HAVE_DECL_RTA_ENCAP #include "vrrp_iproute.h" #endif #include "parser.h" #include "memory.h" bool get_realms(uint32_t *realms, const char *str) { uint32_t val, val1; char *end; if ((end = strchr(str,'/'))) str = STRNDUP(str, end - str); if (!find_rttables_realms(str, &val)) goto err; if (end) { if (!find_rttables_realms(end + 1, &val1)) goto err; val <<= 16; val |= val1; FREE_CONST(str); } *realms = val; return false; err: if (end) FREE_CONST(str); return true; } bool get_u8(uint8_t *val, const char *str, uint8_t max, const char* errmsg) { unsigned t_val; if (read_unsigned(str, &t_val, 0, max, false)) { *val = (uint8_t)t_val; return false; } report_config_error(CONFIG_GENERAL_ERROR, errmsg, str); return true; } bool get_u16(uint16_t *val, const char *str, uint16_t max, const char* errmsg) { unsigned t_val; if (read_unsigned(str, &t_val, 0, max, false)) { *val = (uint16_t)t_val; return false; } report_config_error(CONFIG_GENERAL_ERROR, errmsg, str); return true; } bool get_u32(uint32_t *val, const char *str, uint32_t max, const char* errmsg) { unsigned t_val; if (read_unsigned(str, &t_val, 0, max, false)) { *val = (uint32_t)t_val; return false; } report_config_error(CONFIG_GENERAL_ERROR, errmsg, str); return true; } bool get_u64(uint64_t *val, const char *str, uint64_t max, const char* errmsg) { uint64_t t_val; if (read_unsigned64(str, &t_val, 0, max, false)) { *val = (uint64_t)t_val; return false; } report_config_error(CONFIG_GENERAL_ERROR, errmsg, str); return true; } /* The kernel has the following definitions in include/net/tcp.h: * #define TCP_RTO_MAX ((unsigned)(120*HZ)) * #define TCP_RTO_MIN ((unsigned)(HZ/5)) * so rtt values must be between 0.2 and 120 seconds. */ #define RTT_MIN_MS 200U #define RTT_MAX_MS 120000U bool get_time_rtt(uint32_t *val, const char *str, unsigned unit_mult, const char *type) { unsigned res; unsigned shift; char *p; char *str_cpy; const char *str1; bool ret; bool raw; /* Skip leading whitespace */ str += strspn(str, WHITE_SPACE); /* Have units been specified? */ raw = true; if ((p = strpbrk(str, "sS"))) { if (strcasecmp(p, "s") && strcasecmp(p, "sec") && strcasecmp(p, "secs")) return true; raw = false; if (p > str && tolower(p[-1]) == 'm') { shift = 0; p--; } else shift = 3; if (p == str) return true; str_cpy = MALLOC(p - str + 1); memcpy(str_cpy, str, p - str); str_cpy[p - str] = '\0'; str1 = str_cpy; } else { str_cpy = NULL; str1 = str; shift = 0; } /* We used to support exponential form to match ip route command, but that support was * accidental (e.g. "rtt 1e3" did not work, but "rtt 1.e3" did - exponential form in ip * command is only supported if a decimal point is specified). */ if (strpbrk(str1, "eE")) { report_config_error(CONFIG_GENERAL_ERROR, "%s value exponential form %s no longer supported", type, str); if (str_cpy) FREE(str_cpy); return true; } ret = read_decimal_unsigned(str1, &res, 0, RTT_MAX_MS * unit_mult, shift, false); if (str_cpy) FREE(str_cpy); if (!ret) return true; if (!raw) res *= unit_mult; if (res > RTT_MAX_MS * unit_mult) { report_config_error(CONFIG_GENERAL_ERROR, "%s value %s exceeds maximum %us, resetting", type, str, RTT_MAX_MS / 1000U); res = RTT_MAX_MS * unit_mult; } else if (res < RTT_MIN_MS * unit_mult) { report_config_error(CONFIG_GENERAL_ERROR, "%s value %s below minimum %ums, resetting", type, str, RTT_MIN_MS); res = RTT_MIN_MS * unit_mult; } *val = res; return false; } bool get_addr64(uint64_t *ap, const char *cp) { int i; union { uint16_t v16[4]; uint64_t v64; } val; /* Skip leading whitespace */ cp += strspn(cp, WHITE_SPACE); val.v64 = 0; for (i = 0; i < 4; i++) { unsigned long n; char *endp; if (!isxdigit(*cp)) return true; /* Not a hex digit */ n = strtoul(cp, &endp, 16); if (n > 0xffff) return true; /* bogus network value */ if (endp == cp) /* no digits */ return true; val.v16[i] = htons(n); if (*endp == '\0') { if (i != 3) /* address too short */ return true; break; } if (i == 3 || *endp != ':') return true; /* extra characters */ cp = endp + 1; } *ap = val.v64; return false; } #if HAVE_DECL_RTA_ENCAP && HAVE_DECL_LWTUNNEL_ENCAP_MPLS bool parse_mpls_address(const char *str, encap_mpls_t *mpls) { char *endp; unsigned count; unsigned long label; mpls->num_labels = 0; /* Skip leading whitespace */ str += strspn(str, WHITE_SPACE); for (count = 0; count < MAX_MPLS_LABELS; count++) { if (str[0] == '-') return true; if (strspn(str, WHITE_SPACE)) /* No embedded whitespace */ return true; label = strtoul(str, &endp, 0); if (endp == str) /* no digits */ return true; /* Fail when the label value is out of range */ if (label > UINT32_MAX) return true; if (label & ~(MPLS_LS_LABEL_MASK >> MPLS_LS_LABEL_SHIFT)) return true; mpls->addr[count].entry = htonl((uint32_t)label << MPLS_LS_LABEL_SHIFT); if (*endp == '\0') { mpls->addr[count].entry |= htonl(1 << MPLS_LS_S_SHIFT); mpls->num_labels = count + 1; return false; } /* Bad character in the address */ if (*endp != '/') return true; str = endp + 1; } /* The address was too long */ return true; } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_iptables_calls.c0000664000175000017500000006451414011454461016625 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: iptables manipulation. * * Author: Quentin Armitage, * * 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, */ #include "config.h" #ifdef _HAVE_LIBIPTC_LINUX_NET_IF_H_COLLISION_ /* Linux 4.5 introduced a namespace collision when including * libiptc/libiptc.h due to both net/if.h and linux/if.h * being included. * * See: http://bugzilla.netfilter.org/show_bug.cgi?id=1067 * * Including net/if.h first stops the problem occuring. */ #include #endif #include #include #ifdef _HAVE_LIBIPSET_ #include #include #endif #include #include #include #include #include "vrrp_iptables_calls.h" #include "memory.h" #include "logger.h" #ifdef _LIBIPTC_DYNAMIC_ #include "global_data.h" #endif #include "vrrp_firewall.h" #include "utils.h" /* We sometimes get a resource_busy on iptc_commit. This appears to happen * when someone else is also updating it. * Tests show that the EAGAIN error is generated if someone else did an * update via iptc_commit between us doing iptc_init and iptc_commit, i.e. * if there had been an update since out init prior to our commit. * * Documentation seems to suggest that iptc_init takes a snapshot of the * state of iptables. This fits with the tests, but also means that we could * be interferred with by anyone else doing an update. */ #ifdef _LIBIPTC_DYNAMIC_ #include /* The addresses of the functions we want */ struct iptc_handle *(*iptc_init_addr)(const char *tablename); void (*iptc_free_addr)(struct iptc_handle *h); int (*iptc_is_chain_addr)(const char *chain, struct iptc_handle *const handle); int (*iptc_insert_entry_addr)(const ipt_chainlabel chain, const struct ipt_entry *e, unsigned int rulenum, struct iptc_handle *handle); int (*iptc_append_entry_addr)(const ipt_chainlabel chain, const struct ipt_entry *e, struct iptc_handle *handle); int (*iptc_delete_entry_addr)(const ipt_chainlabel chain, const struct ipt_entry *origfw, unsigned char *matchmask, struct iptc_handle *handle); int (*iptc_commit_addr)(struct iptc_handle *handle); const char *(*iptc_strerror_addr)(int err); struct ip6tc_handle *(*ip6tc_init_addr)(const char *tablename); void (*ip6tc_free_addr)(struct ip6tc_handle *h); int (*ip6tc_is_chain_addr)(const char *chain, struct ip6tc_handle *const handle); int (*ip6tc_insert_entry_addr)(const ip6t_chainlabel chain, const struct ip6t_entry *e, unsigned int rulenum, struct ip6tc_handle *handle); int (*ip6tc_append_entry_addr)(const ip6t_chainlabel chain, const struct ip6t_entry *e, struct ip6tc_handle *handle); int (*ip6tc_delete_entry_addr)(const ip6t_chainlabel chain, const struct ip6t_entry *origfw, unsigned char *matchmask, struct ip6tc_handle *handle); int (*ip6tc_commit_addr)(struct ip6tc_handle *handle); const char *(*ip6tc_strerror_addr)(int err); /* We can make it look as though normal linking is being used */ #define iptc_init (*iptc_init_addr) #define iptc_free (*iptc_free_addr) #define iptc_is_chain (*iptc_is_chain_addr) #define iptc_insert_entry (*iptc_insert_entry_addr) #define iptc_append_entry (*iptc_append_entry_addr) #define iptc_delete_entry (*iptc_delete_entry_addr) #define iptc_commit (*iptc_commit_addr) #define iptc_strerror (*iptc_strerror_addr) #define ip6tc_init (*ip6tc_init_addr) #define ip6tc_free (*ip6tc_free_addr) #define ip6tc_is_chain (*ip6tc_is_chain_addr) #define ip6tc_insert_entry (*ip6tc_insert_entry_addr) #define ip6tc_append_entry (*ip6tc_append_entry_addr) #define ip6tc_delete_entry (*ip6tc_delete_entry_addr) #define ip6tc_commit (*ip6tc_commit_addr) #define ip6tc_strerror (*ip6tc_strerror_addr) static void* libip4tc_handle; static void* libip6tc_handle; #endif static void set_iface(char *vianame, unsigned char *mask, const char *iface) { size_t vialen = strlen(iface); memset(vianame, 0, IFNAMSIZ); memset(mask, 0, IFNAMSIZ); strcpy(vianame, iface); if (!vialen) return; memset(mask, 0xFF, vialen + 1); } /* Initializes a new iptables instance and returns an iptables resource associated with the new iptables table */ struct iptc_handle * ip4tables_open(const char* tablename) { struct iptc_handle *h ; if ( !( h = iptc_init ( tablename ) ) ) return NULL ; return h ; } int ip4tables_close(struct iptc_handle* handle, int updated) { int res = 1; int sav_errno ; if (updated) { if ( ( res = iptc_commit ( handle ) ) != 1 ) { sav_errno = errno ; log_message(LOG_INFO, "iptc_commit returned %d: %s", res, iptc_strerror (sav_errno) ); } } iptc_free ( handle ) ; if ( res == 1 ) return 0 ; else return ( sav_errno ) ; } int ip4tables_is_chain(struct iptc_handle* handle, const char* chain_name) { return iptc_is_chain(chain_name, handle); } int ip4tables_process_entry(struct iptc_handle *handle, const char *chain_name, unsigned int rulenum, const char *target_name, const ip_address_t *src_ip_address, const ip_address_t *dst_ip_address, const char *in_iface, const char *out_iface, #ifdef _INCLUDE_UNUSED_CODE_ uint16_t protocol, uint8_t type, #else __attribute__((unused)) uint16_t protocol, __attribute__((unused)) uint8_t type, #endif int cmd, uint8_t flags, bool force) { size_t size; struct ipt_entry *fw; struct xt_entry_target *target; #ifdef _INCLUDE_UNUSED_CODE_ struct xt_entry_match *match ; struct ipt_icmp *icmpinfo; #endif ipt_chainlabel chain; int res; int sav_errno; /* Add an entry */ memset (chain, 0, sizeof (chain)); size = XT_ALIGN (sizeof (struct ipt_entry)) + XT_ALIGN (sizeof (struct xt_entry_target) + 1); #ifdef _INCLUDE_UNUSED_CODE_ if ( protocol == IPPROTO_ICMP ) size += XT_ALIGN ( sizeof(struct xt_entry_match) ) + XT_ALIGN ( sizeof(struct ipt_icmp) ) ; #endif fw = PTR_CAST(struct ipt_entry, MALLOC(size)); fw->target_offset = XT_ALIGN ( sizeof ( struct ipt_entry ) ) ; if ( src_ip_address && src_ip_address->ifa.ifa_family != AF_UNSPEC ) { memcpy(&fw->ip.src, &src_ip_address->u.sin.sin_addr, sizeof ( src_ip_address->u.sin.sin_addr ) ); memset ( &fw->ip.smsk, 0xff, sizeof(fw->ip.smsk)); } if ( dst_ip_address && dst_ip_address->ifa.ifa_family != AF_UNSPEC ) { memcpy(&fw->ip.dst, &dst_ip_address->u.sin.sin_addr, sizeof ( dst_ip_address->u.sin.sin_addr ) ); memset ( &fw->ip.dmsk, 0xff, sizeof(fw->ip.dmsk)); } fw->ip.invflags = flags; if (in_iface) set_iface(fw->ip.iniface, fw->ip.iniface_mask, in_iface); if (out_iface) set_iface(fw->ip.outiface, fw->ip.outiface_mask, out_iface); if ( protocol != IPPROTO_NONE ) { fw->ip.proto = protocol ; // fw->ip.flags |= IP6T_F_PROTO ; // IPv6 only #ifdef _INCLUDE_UNUSED_CODE_ if ( protocol == IPPROTO_ICMP ) { match = PTR_CAST(struct xt_entry_match, ((char*)fw + fw->target_offset)); match->u.match_size = XT_ALIGN(sizeof (struct xt_entry_match)) + XT_ALIGN(sizeof(struct ipt_icmp)); match->u.user.revision = 0; fw->target_offset = (uint16_t)(fw->target_offset + match->u.match_size); strcpy ( match->u.user.name, "icmp" ) ; icmpinfo = PTR_CAST(struct ipt_icmp, match->data); icmpinfo->type = type ; // type to match icmpinfo->code[0] = 0 ; // code lower icmpinfo->code[1] = 0xff ; // code upper icmpinfo->invflags = 0 ; // don't invert } #endif } // target is XTC_LABEL_DROP/XTC_LABEL_ACCEPT fw->next_offset = (uint16_t)size; target = ipt_get_target(fw); target->u.user.target_size = XT_ALIGN (sizeof (struct xt_entry_target) + 1); strcpy_safe(target->u.user.name, target_name); // fw->ip.flags |= IPT_F_GOTO; strcpy_safe(chain, chain_name); // Use iptc_append_entry to add to the chain if (cmd == IPADDRESS_DEL) { unsigned char *matchmask = MALLOC(fw->next_offset); memset(matchmask, 0xff, fw->next_offset); res = iptc_delete_entry(chain, fw, matchmask, handle); FREE(matchmask); } else if (rulenum == APPEND_RULE) res = iptc_append_entry (chain, fw, handle ) ; else res = iptc_insert_entry (chain, fw, rulenum, handle ) ; sav_errno = errno ; FREE(fw); if (res != 1 && (!force || sav_errno != ENOENT)) { log_message(LOG_INFO, "ip4tables_process_entry for chain %s returned %d: %s", chain, res, iptc_strerror (sav_errno) ) ; return sav_errno ; } return 0 ; } /* Initializes a new iptables instance and returns an iptables resource associated with the new iptables table */ struct ip6tc_handle * ip6tables_open(const char* tablename) { struct ip6tc_handle *h ; if ( !( h = ip6tc_init ( tablename ) ) ) return NULL ; return h ; } int ip6tables_close(struct ip6tc_handle* handle, int updated) { int res = 1; int sav_errno ; if (updated) { if ( ( res = ip6tc_commit ( handle ) ) != 1 ) { sav_errno = errno ; log_message(LOG_INFO, "iptc_commit returned %d: %s", res, ip6tc_strerror (sav_errno) ); } } ip6tc_free ( handle ) ; if ( res == 1 ) return 0 ; else return ( sav_errno ) ; } int ip6tables_is_chain(struct ip6tc_handle *handle, const char *chain_name) { return ip6tc_is_chain(chain_name, handle); } int ip6tables_process_entry(struct ip6tc_handle *handle, const char *chain_name, unsigned int rulenum, const char *target_name, const ip_address_t *src_ip_address, const ip_address_t *dst_ip_address, const char *in_iface, const char* out_iface, uint16_t protocol, uint8_t type, int cmd, uint8_t flags, bool force) { size_t size; struct ip6t_entry *fw; struct xt_entry_target *target; struct xt_entry_match *match ; struct ip6t_icmp *icmpinfo; ip6t_chainlabel chain; int res; int sav_errno; /* Add an entry */ memset (chain, 0, sizeof (chain)); size = XT_ALIGN (sizeof (struct ip6t_entry)) + XT_ALIGN (sizeof (struct xt_entry_target) + 1); if ( protocol == IPPROTO_ICMPV6 ) size += XT_ALIGN ( sizeof(struct xt_entry_match) ) + XT_ALIGN ( sizeof(struct ip6t_icmp) ) ; fw = PTR_CAST(struct ip6t_entry, MALLOC(size)); fw->target_offset = XT_ALIGN ( sizeof ( struct ip6t_entry ) ) ; if ( src_ip_address && src_ip_address->ifa.ifa_family != AF_UNSPEC ) { memcpy(&fw->ipv6.src, &src_ip_address->u.sin6_addr, sizeof ( src_ip_address->u.sin6_addr ) ); memset ( &fw->ipv6.smsk, 0xff, sizeof(fw->ipv6.smsk)); } if ( dst_ip_address && dst_ip_address->ifa.ifa_family != AF_UNSPEC ) { memcpy(&fw->ipv6.dst, &dst_ip_address->u.sin6_addr, sizeof ( dst_ip_address->u.sin6_addr ) ); memset ( &fw->ipv6.dmsk, 0xff, sizeof(fw->ipv6.dmsk)); } fw->ipv6.invflags = flags; if (in_iface) set_iface(fw->ipv6.iniface, fw->ipv6.iniface_mask, in_iface); if (out_iface) set_iface(fw->ipv6.outiface, fw->ipv6.outiface_mask, out_iface); if ( protocol != IPPROTO_NONE ) { fw->ipv6.proto = protocol ; fw->ipv6.flags |= IP6T_F_PROTO ; // IPv6 only if ( protocol == IPPROTO_ICMPV6 ) { match = PTR_CAST(struct xt_entry_match, ((char*)fw + fw->target_offset)); match->u.match_size = XT_ALIGN ( sizeof (struct xt_entry_match) ) + XT_ALIGN ( sizeof (struct ip6t_icmp) ) ; match->u.user.revision = 0; fw->target_offset = (uint16_t)(fw->target_offset + match->u.match_size); strcpy ( match->u.user.name, "icmp6" ) ; icmpinfo = PTR_CAST(struct ip6t_icmp, match->data); icmpinfo->type = type ; // type to match icmpinfo->code[0] = 0 ; // code lower icmpinfo->code[1] = 0xff ; // code upper icmpinfo->invflags = 0 ; // don't invert } } // target is XTC_LABEL_DROP/XTC_LABEL_ACCEPT fw->next_offset = (uint16_t)size; target = ip6t_get_target ( fw ) ; target->u.user.target_size = XT_ALIGN (sizeof (struct xt_entry_target) + 1); strcpy_safe(target->u.user.name, target_name); // fw->ip.flags |= IPT_F_GOTO; strcpy_safe(chain, chain_name); // Use iptc_append_entry to add to the chain if (cmd == IPADDRESS_DEL) { unsigned char *matchmask = MALLOC(fw->next_offset); memset(matchmask, 0xff, fw->next_offset); res = ip6tc_delete_entry ( chain, fw, matchmask, handle); FREE(matchmask); } else if (rulenum == APPEND_RULE) res = ip6tc_append_entry (chain, fw, handle ) ; else res = ip6tc_insert_entry (chain, fw, rulenum, handle ) ; sav_errno = errno ; FREE(fw); if (res != 1 && (!force || sav_errno != ENOENT)) { log_message(LOG_INFO, "ip6tables_process_entry for chain %s returned %d: %s", chain, res, ip6tc_strerror (sav_errno) ) ; return sav_errno ; } return 0 ; } #ifdef _HAVE_LIBIPSET_ static int get_version(unsigned int* version) { int sockfd = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW); struct ip_set_req_version req_version; socklen_t size = sizeof(req_version); int res; if (sockfd < 0) { log_message(LOG_INFO, "Can't open socket to ipset."); return -1; } req_version.op = IP_SET_OP_VERSION; res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size); if (res != 0) log_message(LOG_INFO, "Kernel module xt_set is not loaded in."); *version = req_version.version; return sockfd; } static void get_set_byname_only(const char *setname, struct xt_set_info *info, int sockfd, unsigned int version, bool ignore_errors) { struct ip_set_req_get_set req = { .version = version }; socklen_t size = sizeof(struct ip_set_req_get_set); int res; req.op = IP_SET_OP_GET_BYNAME; strncpy(req.set.name, setname, IPSET_MAXNAMELEN); req.set.name[IPSET_MAXNAMELEN - 1] = '\0'; res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size); if (res != 0) { if (!ignore_errors) log_message(LOG_INFO, "Problem when communicating with ipset, errno=%d.", errno); } else if (size != sizeof(struct ip_set_req_get_set)) { if (!ignore_errors) log_message(LOG_INFO, "Incorrect return size from kernel during ipset lookup, " "(want %zu, got %zu)", sizeof(struct ip_set_req_get_set), (size_t)size); } else if (req.set.index == IPSET_INVALID_ID) { if (!ignore_errors) log_message(LOG_INFO, "Set %s doesn't exist.", setname); } else info->index = req.set.index; } static void get_set_byname(const char *setname, struct xt_set_info *info, unsigned family, bool ignore_errors) { #if defined IP_SET_OP_GET_FNAME struct ip_set_req_get_set_family req; socklen_t size = sizeof(struct ip_set_req_get_set_family); int res; #else if (family) {}; /* Avoid compiler warning */ #endif int sockfd; unsigned int version; info->index = IPSET_INVALID_ID; if ((sockfd = get_version(&version)) == -1) { info->index = IPSET_INVALID_ID; return; } #if defined IP_SET_OP_GET_FNAME /* Since Linux 3.13 */ req.version = version; req.op = IP_SET_OP_GET_FNAME; strncpy(req.set.name, setname, IPSET_MAXNAMELEN); req.set.name[IPSET_MAXNAMELEN - 1] = '\0'; res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size); if (res != 0 && errno == EBADMSG) #endif { /* Backward compatibility */ get_set_byname_only(setname, info, sockfd, version, ignore_errors); close(sockfd); return; } #if defined IP_SET_OP_GET_FNAME close(sockfd); if (res != 0) { if (!ignore_errors) log_message(LOG_INFO, "Problem when communicating with ipset, errno=%d.", errno); } else if (size != sizeof(struct ip_set_req_get_set_family)) { if (!ignore_errors) log_message(LOG_INFO, "Incorrect return size from kernel during ipset lookup, " "(want %zu, got %zu)", sizeof(struct ip_set_req_get_set_family), (size_t)size); } else if (req.set.index == IPSET_INVALID_ID) { if (!ignore_errors) log_message(LOG_INFO, "Set %s doesn't exist.", setname); } else if (!(req.family == family || req.family == NFPROTO_UNSPEC)) { if (!ignore_errors) log_message(LOG_INFO, "The protocol family of set %s is %s, " "which is not applicable.", setname, req.family == NFPROTO_IPV4 ? "IPv4" : "IPv6"); } else info->index = req.set.index; #endif } int ip4tables_add_rules(struct iptc_handle *handle, const char *chain_name, unsigned int rulenum, uint8_t dim, uint8_t src_dst, const char* target_name, const ip_address_t *src_ip_address, const ip_address_t *dst_ip_address, const char *set_name, #ifdef _INCLUDE_UNUSED_CODE_ uint16_t protocol, uint8_t param, #else __attribute__((unused)) uint16_t protocol, __attribute__((unused)) uint8_t param, #endif int cmd, bool ignore_errors) { size_t size; struct ipt_entry *fw; struct xt_entry_target *target; struct xt_entry_match *match; #ifdef HAVE_XT_SET_INFO_MATCH_V4 struct xt_set_info_match_v4 *setinfo; #else struct xt_set_info_match_v3 *setinfo; #endif ipt_chainlabel chain; int res; int sav_errno; /* Add an entry */ size = XT_ALIGN(sizeof (struct ipt_entry)) + XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(struct xt_entry_target) + 1) + XT_ALIGN(sizeof(*setinfo)); #ifdef _INCLUDE_UNUSED_CODE_ if (protocol == IPPROTO_ICMP) size += XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(struct ipt_icmp)); #endif fw = PTR_CAST(struct ipt_entry, MALLOC(size)); fw->target_offset = XT_ALIGN(sizeof(struct ipt_entry)); if (src_ip_address && src_ip_address->ifa.ifa_family != AF_UNSPEC) { fw->ip.src.s_addr = src_ip_address->u.sin.sin_addr.s_addr; fw->ip.smsk.s_addr = 0xffffffff; } if (dst_ip_address && dst_ip_address->ifa.ifa_family != AF_UNSPEC) { fw->ip.dst.s_addr = dst_ip_address->u.sin.sin_addr.s_addr; fw->ip.dmsk.s_addr = 0xffffffff; } // set match = PTR_CAST(struct xt_entry_match, ((char*)fw + fw->target_offset)); match->u.match_size = XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(*setinfo)); #ifdef HAVE_XT_SET_INFO_MATCH_V4 match->u.user.revision = 4; #elif defined HAVE_XT_SET_INFO_MATCH_V3 match->u.user.revision = 3; #else match->u.user.revision = 1; #endif fw->target_offset = (uint16_t)(fw->target_offset + match->u.match_size); strcpy(match->u.user.name, "set"); #ifdef HAVE_XT_SET_INFO_MATCH_V4 setinfo = PTR_CAST(struct xt_set_info_match_v4, match->data); #else setinfo = PTR_CAST(struct xt_set_info_match_v3, match->data); #endif get_set_byname(set_name, &setinfo->match_set, NFPROTO_IPV4, ignore_errors); if (setinfo->match_set.index == IPSET_INVALID_ID) { FREE(fw); return -1; } setinfo->match_set.dim = dim; setinfo->match_set.flags = src_dst; if (protocol != IPPROTO_NONE) { fw->ip.proto = protocol; #ifdef _INCLUDE_UNUSED_CODE_ // fw->ip.flags |= IP6T_F_PROTO ; // IPv6 only if (protocol == IPPROTO_ICMP) { match = PTR_CAST(struct xt_entry_match, ((char*)fw + fw->target_offset)); match->u.match_size = XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(struct ipt_icmp)); match->u.user.revision = 0; fw->target_offset = (uint16_t)(fw->target_offset + match->u.match_size); strcpy(match->u.user.name, "icmp"); struct ipt_icmp *icmpinfo = PTR_CAST(struct ipt_icmp, match->data); icmpinfo->type = param; // type to match icmpinfo->code[0] = 0; // code lower icmpinfo->code[1] = 0xff; // code upper icmpinfo->invflags = 0; // don't invert } #endif } // target is XTC_LABEL_DROP/XTC_LABEL_ACCEPT fw->next_offset = (uint16_t)size; target = ipt_get_target(fw); target->u.user.target_size = XT_ALIGN(sizeof(struct xt_entry_target) + 1); strcpy_safe(target->u.user.name, target_name); // fw->ip.flags |= IPT_F_GOTO; strcpy_safe(chain, chain_name); // Use iptc_append_entry to add to the chain if (cmd == IPADDRESS_DEL) { unsigned char *matchmask = MALLOC(fw->next_offset); memset(matchmask, 0xff, fw->next_offset); res = iptc_delete_entry(chain, fw, matchmask, handle); FREE(matchmask); } else if (rulenum == APPEND_RULE) res = iptc_append_entry(chain, fw, handle) ; else res = iptc_insert_entry(chain, fw, rulenum, handle) ; sav_errno = errno; FREE(fw); if (res!= 1) { if (!ignore_errors) log_message(LOG_INFO, "iptc_insert_entry for chain %s returned %d: %s", chain_name, res, iptc_strerror(sav_errno)) ; return sav_errno; } return 0; } int ip6tables_add_rules(struct ip6tc_handle *handle, const char *chain_name, unsigned int rulenum, uint8_t dim, uint8_t src_dst, const char *target_name, const ip_address_t *src_ip_address, const ip_address_t *dst_ip_address, const char *set_name, uint16_t protocol, uint8_t param, int cmd, bool ignore_errors) { size_t size; struct ip6t_entry *fw; struct xt_entry_target *target; struct xt_entry_match *match; #ifdef HAVE_XT_SET_INFO_MATCH_V4 struct xt_set_info_match_v4 *setinfo; #else struct xt_set_info_match_v3 *setinfo; #endif ip6t_chainlabel chain; int res; int sav_errno; /* Add an entry */ memset(chain, 0, sizeof(chain)); size = XT_ALIGN(sizeof (struct ip6t_entry)) + XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(struct xt_entry_target) + 1) + XT_ALIGN(sizeof(*setinfo)); if (protocol == IPPROTO_ICMPV6) size += XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(struct ip6t_icmp)); fw = PTR_CAST(struct ip6t_entry, MALLOC(size)); if (src_ip_address && src_ip_address->ifa.ifa_family != AF_UNSPEC) { // memcpy(&fw->ipv6.src, &src_ip_address->u.sin6_addr, sizeof(src_ip_address->u.sin6_addr)); fw->ipv6.src = src_ip_address->u.sin6_addr; memset(&fw->ipv6.smsk, 0xff, sizeof(fw->ipv6.smsk)); } if ( dst_ip_address && dst_ip_address->ifa.ifa_family != AF_UNSPEC ) { // memcpy(&fw->ipv6.dst, &dst_ip_address->u.sin6_addr, sizeof ( dst_ip_address->u.sin6_addr ) ); fw->ipv6.dst = dst_ip_address->u.sin6_addr; memset(&fw->ipv6.dmsk, 0xff, sizeof(fw->ipv6.dmsk)); } fw->target_offset = XT_ALIGN(sizeof(struct ip6t_entry)); // set match = PTR_CAST(struct xt_entry_match, ((char*)fw + fw->target_offset)); match->u.match_size = XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(*setinfo)); #ifdef HAVE_XT_SET_INFO_MATCH_V4 match->u.user.revision = 4; #elif defined HAVE_XT_SET_INFO_MATCH_V3 match->u.user.revision = 3; #else match->u.user.revision = 1; #endif fw->target_offset = (uint16_t)(fw->target_offset + match->u.match_size); strcpy(match->u.user.name, "set"); #ifdef HAVE_XT_SET_INFO_MATCH_V4 setinfo = PTR_CAST(struct xt_set_info_match_v4, match->data); #else setinfo = PTR_CAST(struct xt_set_info_match_v3, match->data); #endif get_set_byname (set_name, &setinfo->match_set, NFPROTO_IPV6, ignore_errors); if (setinfo->match_set.index == IPSET_INVALID_ID) { FREE(fw); return -1; } setinfo->match_set.dim = dim; setinfo->match_set.flags = src_dst; if (protocol != IPPROTO_NONE) { fw->ipv6.proto = protocol; fw->ipv6.flags |= IP6T_F_PROTO ; // IPv6 only if (protocol == IPPROTO_ICMPV6) { match = PTR_CAST(struct xt_entry_match, ((char*)fw + fw->target_offset)); match->u.match_size = XT_ALIGN(sizeof(struct xt_entry_match)) + XT_ALIGN(sizeof(struct ip6t_icmp)); match->u.user.revision = 0; fw->target_offset = (uint16_t)(fw->target_offset + match->u.match_size); strcpy(match->u.user.name, "icmp6"); struct ip6t_icmp *icmpinfo = PTR_CAST(struct ip6t_icmp, match->data); icmpinfo->type = param; // type to match icmpinfo->code[0] = 0; // code lower icmpinfo->code[1] = 0xff; // code upper icmpinfo->invflags = 0; // don't invert } } // target is XTC_LABEL_DROP/XTC_LABEL_ACCEPT fw->next_offset = (uint16_t)size; target = ip6t_get_target(fw); target->u.user.target_size = XT_ALIGN(sizeof(struct xt_entry_target) + 1); strcpy_safe(target->u.user.name, target_name); // fw->ip.flags |= IP6T_F_GOTO; strcpy_safe(chain, chain_name); // Use iptc_append_entry to add to the chain if (cmd == IPADDRESS_DEL) { unsigned char *matchmask = MALLOC(fw->next_offset); memset(matchmask, 0xff, fw->next_offset); res = ip6tc_delete_entry(chain, fw, matchmask, handle); FREE(matchmask); } else if (rulenum == APPEND_RULE) res = ip6tc_append_entry(chain, fw, handle) ; else res = ip6tc_insert_entry(chain, fw, rulenum, handle) ; sav_errno = errno; FREE(fw); if (res!= 1) { if (!ignore_errors) log_message(LOG_INFO, "ip6tc_insert_entry for chain %s returned %d: %s", chain, res, ip6tc_strerror(sav_errno)) ; return sav_errno; } return 0; } #endif #ifdef _LIBIPTC_DYNAMIC_ bool iptables_lib_init(uint8_t family) { if (family == AF_INET) { if (libip4tc_handle) return true; /* Attempt to open the ip4tc library */ if (!(libip4tc_handle = dlopen("libip4tc.so", RTLD_NOW)) && !(libip4tc_handle = dlopen(IP4TC_LIB_NAME, RTLD_NOW))) { log_message(LOG_INFO, "Unable to load ip4tc library - %s", dlerror()); } else if (!(iptc_init_addr = dlsym(libip4tc_handle, "iptc_init")) || !(iptc_free_addr = dlsym(libip4tc_handle, "iptc_free")) || !(iptc_is_chain_addr = dlsym(libip4tc_handle,"iptc_is_chain")) || !(iptc_insert_entry_addr = dlsym(libip4tc_handle,"iptc_insert_entry")) || !(iptc_append_entry_addr = dlsym(libip4tc_handle,"iptc_append_entry")) || !(iptc_delete_entry_addr = dlsym(libip4tc_handle,"iptc_delete_entry")) || !(iptc_commit_addr = dlsym(libip4tc_handle,"iptc_commit")) || !(iptc_strerror_addr = dlsym(libip4tc_handle,"iptc_strerror"))) { log_message(LOG_INFO, "Failed to dynamic link an iptc function - %s", dlerror()); dlclose(libip4tc_handle); libip4tc_handle = NULL; } return !!libip4tc_handle; } if (libip6tc_handle) return true; /* Attempt to open the ip6tc library */ if (!(libip6tc_handle = dlopen("libip6tc.so", RTLD_NOW)) && !(libip6tc_handle = dlopen(IP6TC_LIB_NAME, RTLD_NOW))) { log_message(LOG_INFO, "Unable to load ip6tc library - %s", dlerror()); } else if (!(ip6tc_init_addr = dlsym(libip6tc_handle, "ip6tc_init")) || !(ip6tc_free_addr = dlsym(libip6tc_handle, "ip6tc_free")) || !(ip6tc_is_chain_addr = dlsym(libip6tc_handle,"ip6tc_is_chain")) || !(ip6tc_insert_entry_addr = dlsym(libip6tc_handle,"ip6tc_insert_entry")) || !(ip6tc_append_entry_addr = dlsym(libip6tc_handle,"ip6tc_append_entry")) || !(ip6tc_delete_entry_addr = dlsym(libip6tc_handle,"ip6tc_delete_entry")) || !(ip6tc_commit_addr = dlsym(libip6tc_handle,"ip6tc_commit")) || !(ip6tc_strerror_addr = dlsym(libip6tc_handle,"ip6tc_strerror"))) { log_message(LOG_INFO, "Failed to dynamic link an ip6tc function - %s", dlerror()); dlclose(libip6tc_handle); libip6tc_handle = NULL; } return !!libip6tc_handle; } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_nftables.c0000664000175000017500000016361014650126247015446 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_nftables.c * * Author: Quentin Armitage, * * 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-2020 Alexandre Cassen, */ /* Up to commit 0ec6c01f this used libnftnl/libmnl, but that had overheads, * and constructing the netlink packets directly works just as well. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_NFTNL_UDATA #include #endif #include #include #include #include #include #include #include #include #include #ifdef _WITH_LVS_ #include #include #endif #include #include "nftables.h" #include "vrrp_nftables.h" #include "logger.h" #include "vrrp.h" #include "vrrp_ipaddress.h" #include "global_data.h" #include "list_head.h" #include "utils.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_firewall.h" #endif #define ICMPV6_ROUTER_SOLICIT 133 #ifdef _HAVE_VRRP_VMAC_ #if HAVE_DECL_NFTA_DUP_MAX static const char vmac_map_name[] = "vmac_map"; static const char vmac_set_name[] = "vmac_set"; #else static const char vmac_set_name[] = "vmac_set"; static const char *vmac_map_name = vmac_set_name; #endif static const char parent_link_local_set_name[] = "parent_link_local"; #if HAVE_DECL_NFT_META_OIFKIND static const char macvlan[16] = "macvlan"; #endif #endif static int ifname_type; static bool ipv4_table_setup; static bool ipv4_vips_setup; static bool ipv6_table_setup; static bool ipv6_vips_setup; static bool setup_ll_ifname; static bool setup_ll_ifindex; #ifdef _HAVE_VRRP_VMAC_ static bool ipv4_igmp_setup; static bool ipv6_igmp_setup; #endif static struct nftnl_rule *setup_rule(uint8_t family, const char *table, const char *chain, const char *handle, const char *set, bool saddr, uint32_t verdict, bool neg) { struct nftnl_rule *r = NULL; uint64_t handle_num; r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); if (handle != NULL) { handle_num = atoll(handle); nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle_num); } /* Use nft --debug netlink,mnl to see the netlink message for an nft command. * To monitor another command, use nft monitor --debug mnl. * nft --debug all gives more info * nft monitor --debug all - allows monitoring of iptables-nft etc. * mnl_nlmsg_fprintf is the function that prints it if * we want to view what we have constructed * * The indentation is added to show the nesting. To indent a nested block, * the number of lines to indent is (length / 4 - 1). * ---------------- ------------------ | 0000000020 | | message length | | 00016 | R--- | | type | flags | NFNL_MSG_BATCH_BEGIN | REQUEST | 0000000003 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 00 00 0a 00 | | extra header | family = AF_UNSPEC, version = NFNETLINK_V0 , res_id = NFNL_SUBSYS_NFTABLES ---------------- ------------------ ---------------- ------------------ | 0000000208 | | message length | | 02566 | R--- | | type | flags | NEWRULE | REQUEST | 0000000004 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ |00011|--|00001| |len |flags| type| NFTA_RULE_TABLE nftnl_rule_set_str(r, NFTNL_RULE_TABLE, str); | 66 69 6c 74 | | data | f i l t | 65 72 00 00 | | data | e r |00018|--|00002| |len |flags| type| NFTA_RULE_CHAIN | 6b 65 65 70 | | data | k e e p | 61 6c 69 76 | | data | a l i v | 65 64 5f 69 | | data | e d _ i | 6e 00 00 00 | | data | n |00156|N-|00004| |len |flags| type| NFT_RULE_EXPRESSIONS (see nftnl_rule_nlmsg_build_payload, netlink_gen_expr) |00052|N-|00001| |len |flags| type| NFTA_LIST_ELEM | NEST (to add - nftnl_rule_add_expr) |00012|--|00001| |len |flags| type| NFTA_EXPR_NAME (see netlink_gen_payload) | 70 61 79 6c | | data | p a y l | 6f 61 64 00 | | data | o a d |00036|N-|00002| |len |flags| type| NFTA_EXPR_DATA | NEST |00008|--|00001| |len |flags| type| NFTNL_EXPR_PAYLOAD_DREG | 00 00 00 01 | | data | NFT_REG_1 |00008|--|00002| |len |flags| type| NFTNL_EXPR_PAYLOAD_BASE | 00 00 00 01 | | data | NFT_PAYLOAD_NETWORK_HEADER |00008|--|00003| |len |flags| type| NFTNL_EXPR_PAYLOAD_OFFSET | 00 00 00 10 | | data | offset 16 |00008|--|00004| |len |flags| type| NFTNL_EXPR_PAYLOAD_LEN | 00 00 00 04 | | data | length 4 |00052|N-|00001| |len |flags| type| NFTA_LIST_ELEM | NEST (netlink_gen_set_stmt) |00011|--|00001| |len |flags| type| NFTA_EXPR_NAME (see netlink_gen_lookup) | 6c 6f 6f 6b | | data | l o o k | 75 70 00 00 | | data | u p |00036|N-|00002| |len |flags| type| NFTA_EXPR_DATA | NEST |00008|--|00002| |len |flags| type| NFTNL_EXPR_LOOKUP_SREG | 00 00 00 01 | | data | NFT_REG_1 |00015|--|00001| |len |flags| type| NFTNL_EXPR_LOOKUP_SET | 6b 65 65 70 | | data | k e e p | 61 6c 69 76 | | data | a l i v | 65 64 00 00 | | data | e d |00008|--|00004| |len |flags| type| NFTNL_EXPR_LOOKUP_SET_ID | 00 00 00 01 | | data | set 1 (Appears not needed) |00048|N-|00001| |len |flags| type| NFTA_LIST_ELEM | NEST (netlink_get_verdict_stmt from netlink_gen_stmt) |00014|--|00001| |len |flags| type| NFTA_EXPR_NAME (see netlink_gen_immediate) | 69 6d 6d 65 | | data | i m m e | 64 69 61 74 | | data | d i a t | 65 00 00 00 | | data | e |00028|N-|00002| |len |flags| type| NFTA_EXPR_DATA | NEST |00008|--|00001| |len |flags| type| NFTNL_EXPR_IMM_DREG = NFTA_IMMEDIATE_DREG | 00 00 00 00 | | data | NFT_REG_VERDICT |00016|N-|00002| |len |flags| type| NFTNL_EXPR_IMM_VERDICT = NFTA_IMMEDIATE_DATA |00012|N-|00002| |len |flags| type| NFTA_DATA_VERDICT |00008|--|00001| |len |flags| type| NFTA_VERDICT_CODE | 00 00 00 00 | | data | NF_DROP ---------------- ------------------ ---------------- ------------------ | 0000000020 | | message length | | 00017 | R--- | | type | flags | NFNL_MSG_BATCH_END | REQUEST | 0000000005 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 00 00 0a 00 | | extra header | family = AF_UNSPEC, version = NFNETLINK_V0 , res_id = NFNL_SUBSYS_NFTABLES ---------------- ------------------ */ if (family == NFPROTO_IPV4) add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, saddr ? offsetof(struct iphdr, saddr) : offsetof(struct iphdr, daddr), sizeof(uint32_t)); else add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, saddr ? offsetof(struct ip6_hdr, ip6_src) : offsetof(struct ip6_hdr, ip6_dst), sizeof(struct in6_addr)); add_lookup(r, NFT_REG_1, NO_REG, set, 0, neg); add_counter(r); add_immediate_verdict(r, verdict, NULL); return r; } static struct nftnl_rule *setup_rule_if(uint8_t family, const char *table, const char *chain, const char *handle, const char *set, bool saddr, bool use_name, uint32_t verdict, bool neg) { struct nftnl_rule *r = NULL; uint64_t handle_num; /* ---------------- ------------------ | 0000000264 | | message length | | 02566 | R--- | | type | flags | | 0000000004 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 0a 00 00 00 | | extra header | |00015|--|00001| |len |flags| type| NFTA_RULE_TABLE | 6b 65 65 70 | | data | k e e p | 61 6c 69 76 | | data | a l i v | 65 64 00 00 | | data | e d |00018|--|00002| |len |flags| type| NFTA_RULE_CHAIN | 69 6e 5f 6c | | data | i n _ l | 69 6e 6b 5f | | data | i n k _ | 6c 6f 63 61 | | data | l o c a | 6c 00 00 00 | | data | l |00208|N-|00004| |len |flags| type| NFTA_RULE_EXPRESSIONS |00052|N-|00001| |len |flags| type| NFTA_LIST_ELEM |00012|--|00001| |len |flags| type| NFTA_EXPR_NAME | 70 61 79 6c | | data | p a y l | 6f 61 64 00 | | data | o a d |00036|N-|00002| |len |flags| type| NFTA_EXPR_DATA |00008|--|00001| |len |flags| type| DREG | 00 00 00 01 | | data | NFT_REG_1 |00008|--|00002| |len |flags| type| BASE | 00 00 00 01 | | data | NFT_PAYLOAD_NETWORK_HEADER |00008|--|00003| |len |flags| type| OFFSET | 00 00 00 18 | | data | |00008|--|00004| |len |flags| type| LEN | 00 00 00 10 | | data | |00036|N-|00001| |len |flags| type| NFTA_LIST_ELEM |00009|--|00001| |len |flags| type| NFTA_EXPR_NAME | 6d 65 74 61 | | data | m e t a | 00 00 00 00 | | data | |00020|N-|00002| |len |flags| type| NFTA_EXPR_DATA |00008|--|00002| |len |flags| type| NFTA_META_KEY | 00 00 00 06 | | data | NFT_META_IIFNAME |00008|--|00001| |len |flags| type| NFTA_META_DREG | 00 00 00 02 | | data |NFT_REG_2 |00048|N-|00001| |len |flags| type| NFTA_LIST_ELEM |00011|--|00001| |len |flags| type| NFTA_EXPR_NAME | 6c 6f 6f 6b | | data | l o o k | 75 70 00 00 | | data | u p |00032|N-|00002| |len |flags| type| NFTA_EXPR_DATA |00008|--|00002| |len |flags| type| NFTA_LOOKUP_SREG | 00 00 00 01 | | data | NFT_REG_1 |00010|--|00001| |len |flags| type| NFTA_LOOKUP_SET | 69 66 5f 6c | | data | i f _ l | 6c 00 00 00 | | data | l |00008|--|00004| |len |flags| type| NFTA_LOOKUP_SET_ID | 00 00 00 06 | | data | |00020|N-|00001| |len |flags| type| NFTA_LIST_ELEM |00012|--|00001| |len |flags| type| NFTA_EXPR_NAME | 63 6f 75 6e | | data | c o u n | 74 65 72 00 | | data | t e r |00004|N-|00002| |len |flags| type| NFTA_COUNTER_PACKETS |00048|N-|00001| |len |flags| type| NFTA_LIST_ELEM |00014|--|00001| |len |flags| type| NFTA_EXPR_NAME | 69 6d 6d 65 | | data | i m m e | 64 69 61 74 | | data | d i a t | 65 00 00 00 | | data | e |00028|N-|00002| |len |flags| type| NFTA_EXPR_DATA |00008|--|00001| |len |flags| type| NFTA_IMMEDIATE_DREG | 00 00 00 00 | | data | NFT_REG_VERDICT |00016|N-|00002| |len |flags| type| NFTA_IMMEDIATE_DATA |00012|N-|00002| |len |flags| type| NFTA_DATA_VERDICT |00008|--|00001| |len |flags| type| NFTA_VERDICT_CODE | 00 00 00 00 | | data | NF_DROP ---------------- ------------------ */ r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); if (handle != NULL) { handle_num = atoll(handle); nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle_num); } if (family == NFPROTO_IPV4) add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, saddr ? offsetof(struct iphdr, saddr) : offsetof(struct iphdr, daddr), sizeof(uint32_t)); else add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, saddr ? offsetof(struct ip6_hdr, ip6_src) : offsetof(struct ip6_hdr, ip6_dst), sizeof(struct in6_addr)); if (saddr) add_meta(r, use_name ? NFT_META_OIFNAME : NFT_META_OIF, NFT_REG_2); else add_meta(r, use_name ? NFT_META_IIFNAME : NFT_META_IIF, NFT_REG_2); add_lookup(r, NFT_REG_1, NO_REG, set, 0, neg); add_counter(r); add_immediate_verdict(r, verdict, NULL); return r; } static struct nftnl_rule *setup_rule_range_goto(uint8_t family, const char *table, const char *chain, const char *handle, const char *chain_dest, bool saddr) { struct nftnl_rule *r = NULL; uint64_t handle_num; struct in6_addr ip6 = { .s6_addr32[0] = htonl(0xffc00000) }; struct in6_addr ip6_xor = { .s6_addr32[0] = 0 }; /* nft add rule ip6 TABLE CHAIN ip6 {s|d}addr fe80::/10 goto CHAIN_DEST */ r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); /* ---------------- ------------------ | 0000000020 | | message length | | 00016 | R--- | | type | flags | | 0000000003 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 00 00 0a 00 | | extra header | ---------------- ------------------ ---------------- ------------------ | 0000000292 | | message length | | 02566 | R--- | | type | flags | NFT_MSG_NEWRULE | 0000000004 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 0a 00 00 00 | | extra header | |00011|--|00001| |len |flags| type| | 66 69 6c 74 | | data | f i l t | 65 72 00 00 | | data | e r |00018|--|00002| |len |flags| type| | 6b 65 65 70 | | data | k e e p | 61 6c 69 76 | | data | a l i v | 65 64 5f 69 | | data | e d _ i | 6e 00 00 00 | | data | n |00204|N-|00004| |len |flags| type| |00052|N-|00001| |len |flags| type| |00012|--|00001| |len |flags| type| | 70 61 79 6c | | data | p a y l | 6f 61 64 00 | | data | o a d |00036|N-|00002| |len |flags| type| EXPR_DATA |00008|--|00001| |len |flags| type| NFTNL_EXPR_PAYLOAD_DREG 1 | 00 00 00 01 | | data | |00008|--|00002| |len |flags| type| NFTNL_EXPR_PAYLOAD_BASE 1 | 00 00 00 01 | | data | NFT_PAYLOAD_NETWORK_HEADER |00008|--|00003| |len |flags| type| NFTNL_EXPR_PAYLOAD_PAYLOAD OFFSET 24 | 00 00 00 18 | | data | |00008|--|00004| |len |flags| type| NFTNL_EXPR_PAYLOAD_PAYLOAD_LEN 16 | 00 00 00 10 | | data | |00092|N-|00001| |len |flags| type| NFTA_LIST_ELEM |00012|--|00001| |len |flags| type| | 62 69 74 77 | | data | b i t w | 69 73 65 00 | | data | i s e |00076|N-|00002| |len |flags| type| |00008|--|00001| |len |flags| type| | 00 00 00 01 | | data | NFTA_BITWISE_SREG = NFT_REG_1 |00008|--|00002| |len |flags| type| | 00 00 00 01 | | data | NFTA_BITWISE_DREG = NFT_REG_1 |00008|--|00003| |len |flags| type| | 00 00 00 10 | | data | NFTA_BITWISE_LEN = 16 |00024|N-|00004| |len |flags| type| NFTA_BITWISE_MASK = ffc0:: |00020|--|00001| |len |flags| type| | ff c0 00 00 | | data | | 00 00 00 00 | | data | | 00 00 00 00 | | data | | 00 00 00 00 | | data | |00024|N-|00005| |len |flags| type| NFTA_BITWISE_xor = :: |00020|--|00001| |len |flags| type| | 00 00 00 00 | | data | | 00 00 00 00 | | data | | 00 00 00 00 | | data | | 00 00 00 00 | | data | |00056|N-|00001| |len |flags| type| NFTA_LIST_ELEM |00008|--|00001| |len |flags| type| | 63 6d 70 00 | | data | c m p |00044|N-|00002| |len |flags| type| |00008|--|00001| |len |flags| type| | 00 00 00 01 | | data | NFT_CMP_SREG = NFT_REG_1 |00008|--|00002| |len |flags| type| | 00 00 00 00 | | data | NFT_CMP_OP = NFT_CMP_EQ |00024|N-|00003| |len |flags| type| NFT_CMP_DATA |00020|--|00001| |len |flags| type| fe80:: | fe 80 00 00 | | data | | 00 00 00 00 | | data | | 00 00 00 00 | | data | | 00 00 00 00 | | data | |00072|N-|00001| |len |flags| type| NFTA_LIST_ELEM |00014|--|00001| |len |flags| type| NFTA_EXPR_NAME | 69 6d 6d 65 | | data | i m m e | 64 69 61 74 | | data | d i a t | 65 00 00 00 | | data | e |00052|N-|00002| |len |flags| type| NFTA_EXPR_DATA |00008|--|00001| |len |flags| type| NFTA_IMMEDIATE_DREG | 00 00 00 00 | | data | NFT_REG_VERDICT |00040|N-|00002| |len |flags| type| NFTNL_EXPR_IMM_VERDICT |00036|N-|00002| |len |flags| type| NFTA_DATA_VERDICT |00008|--|00001| |len |flags| type| NFTA_VERDICT_CODE | ff ff ff fc | | data | NFT_GOTO |00021|--|00002| |len |flags| type| NFTA_VERDICT_DATA | 6b 65 65 70 | | data | k e e p | 61 6c 69 76 | | data | a l i v | 65 64 5f 69 | | data | e d _ i | 6e 5f 6c 6c | | data | n _ l l | 00 00 00 00 | | data | ---------------- ------------------ ---------------- ------------------ | 0000000020 | | message length | | 00017 | R--- | | type | flags | | 0000000005 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 00 00 0a 00 | | extra header | ---------------- ------------------ */ if (handle != NULL) { handle_num = atoll(handle); nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle_num); } add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, saddr ? offsetof(struct ip6_hdr, ip6_src) : offsetof(struct ip6_hdr, ip6_dst), sizeof(struct in6_addr)); /* The following is interpreted as fe80::/10 by nftables */ ip6.s6_addr32[0] = htonl(0xffc00000); ip6.s6_addr32[1] = ip6.s6_addr32[2] = ip6.s6_addr32[3] = 0; add_bitwise(r, NFT_REG_1, NFT_REG_1, sizeof(ip6), &ip6, &ip6_xor); ip6.s6_addr32[0] = htonl(0xfe800000); add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &ip6, sizeof(ip6)); add_counter(r); add_immediate_verdict(r, NFT_GOTO, chain_dest); return r; } static struct nftnl_rule * setup_rule_icmpv6(uint8_t family, const char *table, const char *chain, const char *handle, const char *set, uint32_t set_id, uint32_t verdict, bool neg) { struct nftnl_rule *r = NULL; uint64_t handle_num; struct ip6_hdr ip6; struct icmp6_hdr icmp6; r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); if (handle != NULL) { handle_num = atoll(handle); nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle_num); } ip6.ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6; add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct ip6_hdr, ip6_ctlun.ip6_un1.ip6_un1_nxt), sizeof(ip6.ip6_ctlun.ip6_un1.ip6_un1_nxt)); add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &ip6.ip6_ctlun.ip6_un1.ip6_un1_nxt, sizeof(ip6.ip6_ctlun.ip6_un1.ip6_un1_nxt)); add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, offsetof(struct icmp6_hdr, icmp6_type), sizeof(icmp6.icmp6_type)); add_lookup(r, NFT_REG_1, NO_REG, set, set_id, neg); add_counter(r); add_immediate_verdict(r, verdict, NULL); return r; } #ifdef _INCLUDE_UNUSED_CODE_ static struct nftnl_rule *setup_rule_simple(uint8_t family, const char *table, const char *chain, const char *handle, uint32_t verdict) { struct nftnl_rule *r = NULL; uint64_t handle_num; r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); if (handle != NULL) { handle_num = atoll(handle); nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle_num); } add_counter(r); add_immediate_verdict(r, verdict, NULL); return r; } #endif #ifdef _HAVE_VRRP_VMAC_ static void setup_parent_link_local(struct mnl_nlmsg_batch *batch) { struct nlmsghdr *nlh; struct nftnl_set *s; struct nftnl_rule *r; int type_for_if; uint8_t protocol = IPPROTO_ICMPV6; struct icmp6_hdr icmp6; if (!ifname_type) ifname_type = set_nf_ifname_type(); type_for_if = global_data->vrrp_nf_ifindex ? NFT_TYPE_IFINDEX : ifname_type; #ifdef _HAVE_VRRP_VMAC_ /* nft add set ip6 keepalived parent_link_local { type ipv6_addr . iface_index/name } */ s = setup_set(NFPROTO_IPV6, global_data->vrrp_nf_table_name, parent_link_local_set_name, (NFT_TYPE_IP6ADDR << NFT_TYPE_BITS) | type_for_if, 0, 0); #endif nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_set_nlmsg_build_payload(nlh, s); nftnl_set_free(s); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived out icmpv6 type nd-neighbor-advert [meta oifkind macvlan] ip6 saddr . oif @parent_link_local */ r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, global_data->vrrp_nf_table_name); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, "out"); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, NFPROTO_IPV6); #if HAVE_DECL_NFT_META_L4PROTO add_meta(r, NFT_META_L4PROTO, NFT_REG_1); /* From Linux 3.14 */ #else add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct ip6_hdr, ip6_nxt), sizeof(((struct ip6_hdr *)NULL)->ip6_nxt)); #endif add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &protocol, sizeof(protocol)); add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, offsetof(struct icmp6_hdr, icmp6_type), sizeof(icmp6.icmp6_type)); icmp6.icmp6_type = ND_NEIGHBOR_ADVERT; add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &icmp6.icmp6_type, sizeof(icmp6.icmp6_type)); #if HAVE_DECL_NFT_META_OIFKIND add_meta(r, NFT_META_OIFKIND, NFT_REG_2); add_cmp(r, NFT_REG_2, NFT_CMP_EQ, &macvlan, sizeof(macvlan)); #endif add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct ip6_hdr, ip6_src), sizeof(struct in6_addr)); add_meta(r, global_data->vrrp_nf_ifindex ? NFT_META_OIF : NFT_META_OIFNAME, NFT_REG_2); add_lookup(r, NFT_REG_1, NO_REG, parent_link_local_set_name, 0, false); add_counter(r); add_immediate_verdict(r, NF_DROP, NULL); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); } #endif static void setup_link_local_checks(struct mnl_nlmsg_batch *batch, bool concat_ifname) { const char *set_name = concat_ifname ? "vips_link_local_name" : "vips_link_local"; struct nlmsghdr *nlh; struct nftnl_set *s; struct nftnl_rule *r; int type_for_if; if (!ifname_type) ifname_type = set_nf_ifname_type(); type_for_if = !concat_ifname ? NFT_TYPE_IFINDEX : ifname_type; s = setup_set(NFPROTO_IPV6, global_data->vrrp_nf_table_name, set_name, (NFT_TYPE_IP6ADDR << NFT_TYPE_BITS) | type_for_if, 0, 0); nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_set_nlmsg_build_payload(nlh, s); nftnl_set_free(s); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived in_link_local ip6 daddr . iifname @set_name drop */ r = setup_rule_if(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "in_link_local", NULL, set_name, false, concat_ifname, NF_DROP, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived out_link_local ip6 saddr . oifname @set_name drop */ r = setup_rule_if(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "out_link_local", NULL, set_name, true, concat_ifname, NF_DROP, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); } static int table_cb(__attribute__((unused)) const struct nlmsghdr *nlh, void *data) { *PTR_CAST(bool, data) = true; return MNL_CB_OK; } static bool check_table(uint8_t family, const char *table) { struct nlmsghdr *nlh; struct nftnl_table *t; char buf[64]; bool have_table = false; t = table_add_parse(family, table); nlh = nftnl_table_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, family, NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, t); nftnl_table_free(t); exchange_nl_msg_single(nlh, table_cb, &have_table); return have_table; } static void delete_table(struct mnl_nlmsg_batch *batch, uint8_t family, const char *table) { struct nlmsghdr *nlh; struct nftnl_table *t; log_message(LOG_INFO, "Deleting old ip%s %s table", family == NFPROTO_IPV4 ? "" : "6", table); /* nft delete table ip keepalived - make sure there is no residue*/ t = table_add_parse(family, table); nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, family, NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, t); nftnl_table_free(t); my_mnl_nlmsg_batch_next(batch); } static void check_and_delete_tables(struct mnl_nlmsg_batch *batch, const char *table) { bool have_ipv4_table; bool have_ipv6_table; /* We have to check the tables before adding the delete table entries * into the batch in order to ensure that the seq number doesn't get * out of step in the batch. */ have_ipv4_table = check_table(NFPROTO_IPV4, table); have_ipv6_table = check_table(NFPROTO_IPV6, table); if (have_ipv4_table) delete_table(batch, NFPROTO_IPV4, table); if (have_ipv6_table) delete_table(batch, NFPROTO_IPV6, table); } /* To get the netlink message returned (with the handle), set NLM_F_ECHO in nftnl_..._nlmsg_build_hdr * For some reason, it isn't working for the second batch sent. */ static void nft_setup_ipv4(struct mnl_nlmsg_batch *batch) { struct nlmsghdr *nlh; struct nftnl_table *ta; struct nftnl_chain *t; if (!ipv6_table_setup) check_and_delete_tables(batch, global_data->vrrp_nf_table_name); /* nft add table ip keepalived */ ta = table_add_parse(NFPROTO_IPV4, global_data->vrrp_nf_table_name); nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV4, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, ta); nftnl_table_free(ta); my_mnl_nlmsg_batch_next(batch); /* nft add chain ip keepalived out { type filter hook output priority -1; policy accept } */ t = chain_add_parse(global_data->vrrp_nf_table_name, "out"); if (t == NULL) exit(EXIT_FAILURE); nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_OUT); nftnl_chain_set_str(t, NFTNL_CHAIN_TYPE, "filter"); nftnl_chain_set_s32(t, NFTNL_CHAIN_PRIO, global_data->vrrp_nf_chain_priority); nftnl_chain_set_u32(t, NFTNL_CHAIN_POLICY, NF_ACCEPT); nftnl_chain_nlmsg_build_payload(nlh, t); nftnl_chain_free(t); my_mnl_nlmsg_batch_next(batch); ipv4_table_setup = true; } static void nft_setup_ipv4_vips(struct mnl_nlmsg_batch *batch) { struct nlmsghdr *nlh; struct nftnl_chain *t; struct nftnl_set *s; struct nftnl_rule *r; if (!ipv4_table_setup) nft_setup_ipv4(batch); /* nft add chain ip keepalived in { type filter hook input priority -1; policy accept; } */ t = chain_add_parse(global_data->vrrp_nf_table_name, "in"); if (t == NULL) exit(EXIT_FAILURE); nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV4, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_IN); // input nftnl_chain_set_str(t, NFTNL_CHAIN_TYPE, "filter"); nftnl_chain_set_s32(t, NFTNL_CHAIN_PRIO, global_data->vrrp_nf_chain_priority); nftnl_chain_set_u32(t, NFTNL_CHAIN_POLICY, NF_ACCEPT); nftnl_chain_nlmsg_build_payload(nlh, t); nftnl_chain_free(t); my_mnl_nlmsg_batch_next(batch); /* nft add set ip keepalived vips { type ipv4_addr; } */ s = setup_set(NFPROTO_IPV4, global_data->vrrp_nf_table_name, "vips", NFT_TYPE_IPADDR, 0, 0); nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, NFPROTO_IPV4, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_set_nlmsg_build_payload(nlh, s); nftnl_set_free(s); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip keepalived in ip daddr @vips drop */ r = setup_rule(NFPROTO_IPV4, global_data->vrrp_nf_table_name, "in", NULL, "vips", false, NF_DROP, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip keepalived out ip saddr @vips drop */ r = setup_rule(NFPROTO_IPV4, global_data->vrrp_nf_table_name, "out", NULL, "vips", true, NF_DROP, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); ipv4_vips_setup = true; } static void nft_setup_ipv6(struct mnl_nlmsg_batch *batch) { struct nlmsghdr *nlh; struct nftnl_table *ta; struct nftnl_chain *t; const char *table = global_data->vrrp_nf_table_name; if (!ipv4_table_setup) check_and_delete_tables(batch, global_data->vrrp_nf_table_name); /* nft add table ip6 keepalived */ ta = table_add_parse(NFPROTO_IPV6, table); nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, ta); nftnl_table_free(ta); my_mnl_nlmsg_batch_next(batch); /* nft add chain ip6 keepalived out { type filter hook output priority PRIORITY; policy accept; } */ t = chain_add_parse(table, "out"); if (t == NULL) exit(EXIT_FAILURE); nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_OUT); nftnl_chain_set_str(t, NFTNL_CHAIN_TYPE, "filter"); nftnl_chain_set_s32(t, NFTNL_CHAIN_PRIO, global_data->vrrp_nf_chain_priority); nftnl_chain_set_u32(t, NFTNL_CHAIN_POLICY, NF_ACCEPT); nftnl_chain_nlmsg_build_payload(nlh, t); nftnl_chain_free(t); my_mnl_nlmsg_batch_next(batch); #ifdef _HAVE_VRRP_VMAC_ setup_parent_link_local(batch); #endif ipv6_table_setup = true; } static void nft_setup_ipv6_vips(struct mnl_nlmsg_batch *batch) { struct nlmsghdr *nlh; struct nftnl_set *s; struct nftnl_rule *r; struct nftnl_chain *t; struct nftnl_set_elem *e; struct icmp6_hdr icmp6; if (!ipv6_table_setup) nft_setup_ipv6(batch); /* nft add chain ip6 keepalived in { type filter hook input priority PRIORITY; policy accept; } */ t = chain_add_parse(global_data->vrrp_nf_table_name, "in"); nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_IN); nftnl_chain_set_str(t, NFTNL_CHAIN_TYPE, "filter"); nftnl_chain_set_s32(t, NFTNL_CHAIN_PRIO, global_data->vrrp_nf_chain_priority); nftnl_chain_set_u32(t, NFTNL_CHAIN_POLICY, NF_ACCEPT); nftnl_chain_nlmsg_build_payload(nlh, t); nftnl_chain_free(t); my_mnl_nlmsg_batch_next(batch); /* nft add chain ip6 keepalived in_link_local */ t = chain_add_parse(global_data->vrrp_nf_table_name, "in_link_local"); if (t == NULL) exit(EXIT_FAILURE); nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_chain_nlmsg_build_payload(nlh, t); nftnl_chain_free(t); my_mnl_nlmsg_batch_next(batch); /* nft add chain ip6 keepalived out_link_local */ t = chain_add_parse(global_data->vrrp_nf_table_name, "out_link_local"); nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_chain_nlmsg_build_payload(nlh, t); nftnl_chain_free(t); my_mnl_nlmsg_batch_next(batch); /* nft add set ip6 keepalived vips {type ipv6_addr; } */ s = setup_set(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "vips", NFT_TYPE_IP6ADDR, 0, 0); nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_set_nlmsg_build_payload(nlh, s); nftnl_set_free(s); my_mnl_nlmsg_batch_next(batch); /* nft add set ip6 keepalived neighbor-discovery { type icmpv6_type; } */ s = setup_set(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "neighbor-discovery", NFT_TYPE_ICMPV6_TYPE, 0, 0); nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); /* set_id = nftnl_set_get_u32(s, NFTNL_SET_ID); */ nftnl_set_set_u32(s, NFTNL_SET_FLAGS, NFT_SET_CONSTANT); nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, sizeof(icmp6.icmp6_type)); nftnl_set_nlmsg_build_payload(nlh, s); my_mnl_nlmsg_batch_next(batch); /* nft add element ip6 keepalived neighbor-discovery { nd-neighbor-solicit, nd-neighbor-advert } */ nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); e = nftnl_set_elem_alloc(); icmp6.icmp6_type = ND_NEIGHBOR_SOLICIT; nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &icmp6.icmp6_type, sizeof(icmp6.icmp6_type)); nftnl_set_elem_add(s, e); e = nftnl_set_elem_alloc(); icmp6.icmp6_type = ND_NEIGHBOR_ADVERT; nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &icmp6.icmp6_type, sizeof(icmp6.icmp6_type)); nftnl_set_elem_add(s, e); nftnl_set_elems_nlmsg_build_payload(nlh, s); nftnl_set_free(s); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived in type icmpv6 @neighbor-discovery accept */ r = setup_rule_icmpv6(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "in", NULL, "neighbor-discovery", 0, NF_ACCEPT, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived in ip6 daddr fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff goto in_link_local */ r = setup_rule_range_goto(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "in", NULL, "in_link_local", false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived in icmpv6 type @neighbor-discovery accept */ r = setup_rule_icmpv6(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "out", NULL, "neighbor-discovery", 0, NF_ACCEPT, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived out ip6 saddr fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff goto out_link_local */ r = setup_rule_range_goto(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "out", NULL, "out_link_local", true); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived in ip6 daddr @vips drop */ r = setup_rule(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "in", NULL, "vips", false, NF_DROP, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); /* nft add rule ip6 keepalived out ip6 saddr @vips drop */ r = setup_rule(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "out", NULL, "vips", true, NF_DROP, false); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); ipv6_vips_setup = true; } static void nft_update_ipv4_address(struct mnl_nlmsg_batch *batch, ip_address_t *addr, struct nftnl_set **s) { struct nftnl_set_elem *e; if (!ipv4_vips_setup) nft_setup_ipv4_vips(batch); if (!*s) { *s = nftnl_set_alloc(); if (*s == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } nftnl_set_set_str(*s, NFTNL_SET_TABLE, global_data->vrrp_nf_table_name); nftnl_set_set_str(*s, NFTNL_SET_NAME, "vips"); } /* nft add element ip keepalived vips { ADDR } */ e = nftnl_set_elem_alloc(); if (e == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &addr->u.sin.sin_addr.s_addr, sizeof(in_addr_t)); nftnl_set_elem_add(*s, e); } static void nft_update_ipv6_address(struct mnl_nlmsg_batch *batch, ip_address_t *addr, bool dont_track_primary, interface_t *ifp, struct nftnl_set **set_global, struct nftnl_set **set_ll, struct nftnl_set **set_ll_ifname) { struct nftnl_set_elem *e; uint32_t data_buf[(sizeof(struct in6_addr) + IFNAMSIZ + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; struct nftnl_set **s; const char *set_name; bool use_link_name = false; bool is_link_local; uint32_t len; if (!ipv6_vips_setup) nft_setup_ipv6_vips(batch); is_link_local = IN6_IS_ADDR_LINKLOCAL(&addr->u.sin6_addr); if (!is_link_local) { s = set_global; set_name = "vips"; } else if (!global_data->vrrp_nf_ifindex && dont_track_primary && // TODO - why the ifp check ? (addr->ifp == ifp || addr->dont_track)) { s = set_ll_ifname; set_name = "vips_link_local_name"; use_link_name = true; } else { s = set_ll; set_name = "vips_link_local"; } /* Create the specific set if not already done so */ if (is_link_local) { if (use_link_name) { if (!setup_ll_ifname) { setup_link_local_checks(batch, true); setup_ll_ifname = true; } } else { if (!setup_ll_ifindex) { setup_link_local_checks(batch, false); setup_ll_ifindex = true; } } } /* Create set structure if it doesn't already exist */ if (!*s) { *s = nftnl_set_alloc(); if (*s == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } nftnl_set_set_str(*s, NFTNL_SET_TABLE, global_data->vrrp_nf_table_name); nftnl_set_set_str(*s, NFTNL_SET_NAME, set_name); } /* Add element to set * nft add element ip6 keepalived vips ADDR or * nft add element ip6 keepalived vips_link_local ADDR . IF or * nft add element ip6 keepalived vips_link_local_name ADDR . IF */ e = nftnl_set_elem_alloc(); if (e == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } data_buf[0] = addr->u.sin6_addr.s6_addr32[0]; data_buf[1] = addr->u.sin6_addr.s6_addr32[1]; data_buf[2] = addr->u.sin6_addr.s6_addr32[2]; data_buf[3] = addr->u.sin6_addr.s6_addr32[3]; len = sizeof(struct in6_addr); if (is_link_local) { if (use_link_name) { memset(&data_buf[4], 0, IFNAMSIZ); memcpy(&data_buf[4], addr->ifp->ifname, strlen(addr->ifp->ifname)); len += IFNAMSIZ; } else { data_buf[4] = addr->ifp->ifindex; len += sizeof(data_buf[4]); } } nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &data_buf, len); nftnl_set_elem_add(*s, e); } static void nft_update_addresses(const vrrp_t *vrrp, int cmd) { struct mnl_nlmsg_batch *batch = NULL; struct nlmsghdr *nlh; ip_address_t *ip_addr; struct nftnl_set *ipv4_set = NULL; struct nftnl_set *ipv6_set = NULL; struct nftnl_set *ipv6_ll_index_set = NULL; struct nftnl_set *ipv6_ll_name_set = NULL; struct nftnl_set **ip_set; bool set_rule = (cmd == NFT_MSG_NEWSETELEM); uint16_t type = (cmd == NFT_MSG_NEWSETELEM) ? NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK : NLM_F_ACK; const list_head_t *vip_list; int proto; for (vip_list = &vrrp->vip; vip_list; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL) { list_for_each_entry(ip_addr, vip_list, e_list) { if (set_rule == ip_addr->nftable_rule_set) continue; if (!batch) batch = nft_start_batch(); if (ip_addr->ifa.ifa_family == AF_INET) nft_update_ipv4_address(batch, ip_addr, &ipv4_set); else nft_update_ipv6_address(batch, ip_addr, __test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags), vrrp->ifp, &ipv6_set, &ipv6_ll_index_set, &ipv6_ll_name_set); ip_addr->nftable_rule_set = set_rule; } } if (!batch) return; for (ip_set = &ipv4_set, proto = NFPROTO_IPV4; ip_set; ip_set = ip_set == &ipv4_set ? &ipv6_set : ip_set == &ipv6_set ? &ipv6_ll_index_set : ip_set == &ipv6_ll_index_set ? &ipv6_ll_name_set : NULL) { if (*ip_set) { nlh = nftnl_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), cmd, proto, type, seq++); nftnl_set_elems_nlmsg_build_payload(nlh, *ip_set); nftnl_set_free(*ip_set); my_mnl_nlmsg_batch_next(batch); } proto = NFPROTO_IPV6; } nft_end_batch(batch, false); } void nft_add_addresses(vrrp_t *vrrp) { nft_update_addresses(vrrp, NFT_MSG_NEWSETELEM); } void nft_remove_addresses(vrrp_t *vrrp) { if (!nl) return; // Should delete tables nft_update_addresses(vrrp, NFT_MSG_DELSETELEM); } void nft_remove_addresses_iplist(list_head_t *l) { vrrp_t vrrp = {0}; /* "Borrow" the list of addresses */ list_copy(&vrrp.vip, l); INIT_LIST_HEAD(&vrrp.evip); nft_update_addresses(&vrrp, NFT_MSG_DELSETELEM); /* Restore the list of addresses */ list_copy(l, &vrrp.vip); } #ifdef _HAVE_VRRP_VMAC_ static struct nftnl_rule * setup_rule_move_igmp(uint8_t family, const char *table, const char *chain, const char *set_icmpv6_type, const char *handle, const char *set_map) { /* If have nft dup statement: nft add rule ip keepalived out ip protocol igmp [meta oifkind macvlan] dup to ip daddr device oif map @vmac_map drop nft add rule ip6 keepalived out icmpv6 type @mld-report [meta oifkind macvlan] dup to ip6 daddr device oif map @vmac_map drop otherwise: nft add rule ip keepalived out ip protocol igmp [meta oifkind macvlan] oif @vmac_set drop nft add rule ip6 keepalived out icmpv6 type @mld-report [meta oifkind macvlan] oif @vmac_set drop * * Note: on 3.13 kernels, icmpv6 is specified as @nh,48,8 58 */ struct nftnl_rule *r = NULL; uint64_t handle_num; uint8_t protocol; struct icmp6_hdr icmp6; r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); if (handle != NULL) { handle_num = atoll(handle); nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle_num); } if (family == NFPROTO_IPV4) { add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct iphdr, protocol), sizeof(uint8_t)); protocol = IPPROTO_IGMP; add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &protocol, sizeof(protocol)); #if HAVE_DECL_NFTA_DUP_MAX add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct iphdr, daddr), sizeof(struct in_addr)); #endif } else { #if HAVE_DECL_NFT_META_L4PROTO add_meta(r, NFT_META_L4PROTO, NFT_REG_1); /* From Linux 3.14 */ #else add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct ip6_hdr, ip6_nxt), sizeof(((struct ip6_hdr *)NULL)->ip6_nxt)); #endif protocol = IPPROTO_ICMPV6; add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &protocol, sizeof(protocol)); add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, offsetof(struct icmp6_hdr, icmp6_type), sizeof(icmp6.icmp6_type)); add_lookup(r, NFT_REG_1, NO_REG, set_icmpv6_type, 0, false); #if HAVE_DECL_NFTA_DUP_MAX add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct ip6_hdr, ip6_dst), sizeof(struct in6_addr)); #endif } #if HAVE_DECL_NFT_META_OIFKIND add_meta(r, NFT_META_OIFKIND, NFT_REG_2); add_cmp(r, NFT_REG_2, NFT_CMP_EQ, &macvlan, sizeof(macvlan)); #endif add_meta(r, NFT_META_OIF, NFT_REG_2); #if HAVE_DECL_NFTA_DUP_MAX add_lookup(r, NFT_REG_2, NFT_REG_2, set_map, 1, false); add_dup(r, NFT_REG_1, NFT_REG_2); #else add_lookup(r, NFT_REG_2, NO_REG, set_map, 1, false); #endif add_counter(r); add_immediate_verdict(r, NF_DROP, NULL); return r; } static struct nftnl_rule * setup_rule_drop_router_solicit(const char *table, const char *chain, const char *handle, const char *set) { /* nft add rule ip6 keepalived out icmpv6 type nd-router-solicit [meta oifkind macvlan] oif set @vmac_map drop */ struct nftnl_rule *r = NULL; uint64_t handle_num; uint8_t protocol; struct icmp6_hdr icmp6; r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, NFPROTO_IPV6); if (handle != NULL) { handle_num = atoll(handle); nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle_num); } #if HAVE_DECL_NFT_META_L4PROTO add_meta(r, NFT_META_L4PROTO, NFT_REG_1); /* From Linux 3.14 */ #else add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct ip6_hdr, ip6_nxt), sizeof(((struct ip6_hdr *)NULL)->ip6_nxt)); #endif protocol = IPPROTO_ICMPV6; add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &protocol, sizeof(protocol)); add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, NFT_REG_1, offsetof(struct icmp6_hdr, icmp6_type), sizeof(icmp6.icmp6_type)); icmp6.icmp6_type = ICMPV6_ROUTER_SOLICIT; add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &icmp6.icmp6_type, sizeof(icmp6.icmp6_type)); #if HAVE_DECL_NFT_META_OIFKIND add_meta(r, NFT_META_OIFKIND, NFT_REG_2); add_cmp(r, NFT_REG_2, NFT_CMP_EQ, &macvlan, sizeof(macvlan)); #endif add_meta(r, NFT_META_OIF, NFT_REG_2); add_lookup(r, NFT_REG_2, NO_REG, set, 1, false); add_counter(r); add_immediate_verdict(r, NF_DROP, NULL); return r; } static void nft_setup_igmp(struct mnl_nlmsg_batch *batch, struct nftnl_set **s, #if !HAVE_DECL_NFTA_DUP_MAX __attribute__((unused)) #endif struct nftnl_set **s1, uint8_t nfproto) { struct nlmsghdr *nlh; struct nftnl_rule *r; struct nftnl_set *s2; struct nftnl_set_elem *e; struct icmp6_hdr icmp6; if (nfproto == NFPROTO_IPV4) { if (!ipv4_table_setup) nft_setup_ipv4(batch); } else { if (!ipv6_table_setup) nft_setup_ipv6(batch); } #if HAVE_DECL_NFTA_DUP_MAX /* nft add map ip keepalived vmac_map { type ifname : ifindex } */ *s = setup_set(nfproto, global_data->vrrp_nf_table_name, vmac_map_name, NFT_TYPE_IFINDEX, NFT_SET_MAP, NFT_TYPE_IFINDEX); #else /* nft add set ip keepalived vmac_set { type ifname } */ *s = setup_set(nfproto, global_data->vrrp_nf_table_name, vmac_map_name, NFT_TYPE_IFINDEX, 0, 0); #endif nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, nfproto, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_set_nlmsg_build_payload(nlh, *s); my_mnl_nlmsg_batch_next(batch); if (nfproto == NFPROTO_IPV6) { #if HAVE_DECL_NFTA_DUP_MAX *s1 = setup_set(nfproto, global_data->vrrp_nf_table_name, vmac_set_name, NFT_TYPE_IFINDEX, 0, 0); nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, nfproto, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_set_nlmsg_build_payload(nlh, *s1); my_mnl_nlmsg_batch_next(batch); #endif /* nft add set ip6 keepalived mld-report { type icmpv6_type; } */ s2 = setup_set(NFPROTO_IPV6, global_data->vrrp_nf_table_name, "mld-report", NFT_TYPE_ICMPV6_TYPE, 0, 0); nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); /* set_id = nftnl_set_get_u32(s, NFTNL_SET_ID); */ nftnl_set_set_u32(s2, NFTNL_SET_FLAGS, NFT_SET_CONSTANT); nftnl_set_set_u32(s2, NFTNL_SET_KEY_LEN, sizeof(icmp6.icmp6_type)); nftnl_set_nlmsg_build_payload(nlh, s2); my_mnl_nlmsg_batch_next(batch); /* nft add element ip6 keepalived mld-report { mld2-listener-report, mld-listener-report } */ nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSETELEM, NFPROTO_IPV6, NLM_F_CREATE|NLM_F_ACK, seq++); e = nftnl_set_elem_alloc(); icmp6.icmp6_type = ICMPV6_MLD2_REPORT; nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &icmp6.icmp6_type, sizeof(icmp6.icmp6_type)); nftnl_set_elem_add(s2, e); e = nftnl_set_elem_alloc(); icmp6.icmp6_type = MLD_LISTENER_REPORT; nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &icmp6.icmp6_type, sizeof(icmp6.icmp6_type)); nftnl_set_elem_add(s2, e); nftnl_set_elems_nlmsg_build_payload(nlh, s2); nftnl_set_free(s2); my_mnl_nlmsg_batch_next(batch); } r = setup_rule_move_igmp(nfproto, global_data->vrrp_nf_table_name, "out", "mld-report", NULL, vmac_map_name); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); if (nfproto == NFPROTO_IPV6) { r = setup_rule_drop_router_solicit(global_data->vrrp_nf_table_name, "out", NULL, vmac_set_name); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); } if (nfproto == NFPROTO_IPV4) ipv4_igmp_setup = true; else ipv6_igmp_setup = true; } static void nft_update_vmac_element(struct mnl_nlmsg_batch *batch, struct nftnl_set *s, ifindex_t vmac_ifindex, #if !HAVE_DECL_NFTA_DUP_MAX __attribute__((unused)) #endif ifindex_t base_ifindex, int cmd, uint8_t nfproto) { struct nlmsghdr *nlh; struct nftnl_set_elem *e; uint16_t type = cmd == NFT_MSG_NEWSETELEM ? NLM_F_CREATE | NLM_F_ACK : NLM_F_ACK; /* nft add element ip keepalived vmac_map { "vrrp.253", "eth0" } */ nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), cmd, nfproto, type, seq++); e = nftnl_set_elem_alloc(); nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &vmac_ifindex, sizeof(vmac_ifindex)); #if HAVE_DECL_NFTA_DUP_MAX if (base_ifindex) nftnl_set_elem_set(e, NFTNL_SET_ELEM_DATA, &base_ifindex, sizeof(base_ifindex)); #endif nftnl_set_elem_add(s, e); nftnl_set_elems_nlmsg_build_payload(nlh, s); my_mnl_nlmsg_batch_next(batch); } static void nft_update_parent_link_local_element(struct mnl_nlmsg_batch *batch, struct nftnl_set *s, const interface_t *ifp, int cmd, uint8_t nfproto) { struct nlmsghdr *nlh; struct nftnl_set_elem *e; uint16_t type = cmd == NFT_MSG_NEWSETELEM ? NLM_F_CREATE | NLM_F_ACK : NLM_F_ACK; uint32_t data_buf[(sizeof(struct in6_addr) + IFNAMSIZ + sizeof(uint32_t) - 1) / sizeof(uint32_t)]; uint32_t len; /* nft add element ip keepalived parent_link_local { fe80::b89a:21ff:fee1:f794, "eth0" } */ nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), cmd, nfproto, type, seq++); data_buf[0] = ifp->base_ifp->sin6_addr.s6_addr32[0]; data_buf[1] = ifp->base_ifp->sin6_addr.s6_addr32[1]; data_buf[2] = ifp->base_ifp->sin6_addr.s6_addr32[2]; data_buf[3] = ifp->base_ifp->sin6_addr.s6_addr32[3]; len = sizeof(struct in6_addr); if (!(e = nftnl_set_elem_alloc())) { log_message(LOG_INFO, "OOM error - %d", errno); return; } if (global_data->vrrp_nf_ifindex) { data_buf[4] = ifp->ifindex; len += sizeof(data_buf[4]); } else { memset(&data_buf[4], 0, IFNAMSIZ); memcpy(&data_buf[4], ifp->ifname, strlen(ifp->ifname)); len += IFNAMSIZ; } nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, &data_buf, len); nftnl_set_elem_add(s, e); nftnl_set_elems_nlmsg_build_payload(nlh, s); my_mnl_nlmsg_batch_next(batch); } static void nft_update_vmac_family(struct mnl_nlmsg_batch *batch, const interface_t *ifp, uint8_t nfproto, int cmd) { struct nftnl_set *s, *s1 = NULL; struct nftnl_set *s2; if ((nfproto == NFPROTO_IPV4 && !ipv4_igmp_setup) || (nfproto == NFPROTO_IPV6 && !ipv6_igmp_setup)) nft_setup_igmp(batch, &s, &s1, nfproto); else { s = nftnl_set_alloc(); if (s == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } nftnl_set_set_str(s, NFTNL_SET_TABLE, global_data->vrrp_nf_table_name); nftnl_set_set_str(s, NFTNL_SET_NAME, vmac_map_name); #if HAVE_DECL_NFTA_DUP_MAX if (nfproto == NFPROTO_IPV6) { s1 = nftnl_set_alloc(); if (s1 == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } nftnl_set_set_str(s1, NFTNL_SET_TABLE, global_data->vrrp_nf_table_name); nftnl_set_set_str(s1, NFTNL_SET_NAME, vmac_set_name); } #endif } nft_update_vmac_element(batch, s, ifp->ifindex, ifp->base_ifp->ifindex, cmd, nfproto); if (nfproto == NFPROTO_IPV6) { #if HAVE_DECL_NFTA_DUP_MAX nft_update_vmac_element(batch, s1, ifp->ifindex, 0, cmd, nfproto); #endif s2 = nftnl_set_alloc(); if (s2 == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } nftnl_set_set_str(s2, NFTNL_SET_TABLE, global_data->vrrp_nf_table_name); nftnl_set_set_str(s2, NFTNL_SET_NAME, parent_link_local_set_name); nft_update_parent_link_local_element(batch, s2, ifp, cmd, nfproto); nftnl_set_free(s2); } nftnl_set_free(s); #if HAVE_DECL_NFTA_DUP_MAX if (s1) nftnl_set_free(s1); #endif } static void nft_update_vmac(struct mnl_nlmsg_batch *batch, const interface_t *ifp, int family, bool other_family, int cmd) { uint8_t nfproto = family == AF_INET ? NFPROTO_IPV4 : NFPROTO_IPV6; nft_update_vmac_family(batch, ifp, nfproto, cmd); if (other_family) nft_update_vmac_family(batch, ifp, nfproto == NFPROTO_IPV4 ? NFPROTO_IPV6 : NFPROTO_IPV4, cmd); } void nft_add_vmac(const interface_t *ifp, int family, bool other_family, const interface_t *old_ifp) { struct mnl_nlmsg_batch *batch; batch = nft_start_batch(); if (old_ifp) nft_update_vmac(batch, old_ifp, family, other_family, NFT_MSG_DELSETELEM); nft_update_vmac(batch, ifp, family, other_family, NFT_MSG_NEWSETELEM); nft_end_batch(batch, false); } void nft_remove_vmac(const interface_t *ifp, int family, bool other_family) { struct mnl_nlmsg_batch *batch; batch = nft_start_batch(); nft_update_vmac(batch, ifp, family, other_family, NFT_MSG_DELSETELEM); nft_end_batch(batch, false); } #endif static void nft_cleanup(void) { /* ---------------- ------------------ | 0000000020 | | message length | | 00016 | R--- | | type | flags | | 0000000003 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 00 00 0a 00 | | extra header | ---------------- ------------------ ---------------- ------------------ | 0000000036 | | message length | | 02562 | R-A- | | type | flags | NFT_MSG_DELTABLE | 0000000004 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 02 00 00 00 | | extra header | |00015|--|00001| |len |flags| type| | 6b 65 65 70 | | data | k e e p | 61 6c 69 76 | | data | a l i v | 65 64 00 00 | | data | e d ---------------- ------------------ ---------------- ------------------ | 0000000020 | | message length | | 00017 | R--- | | type | flags | | 0000000005 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 00 00 0a 00 | | extra header | ---------------- ------------------ */ struct nftnl_table *t; struct nlmsghdr *nlh; struct mnl_nlmsg_batch *batch; if (!ipv4_table_setup && !ipv6_table_setup) return; batch = nft_start_batch(); /* nft delete table ip keepalived */ t = nftnl_table_alloc(); nftnl_table_set_str(t, NFTNL_TABLE_NAME, global_data->vrrp_nf_table_name); if (ipv4_table_setup) { nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV4, NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, t); my_mnl_nlmsg_batch_next(batch); } /* nft delete table ip6 keepalived */ if (ipv6_table_setup) { nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV6, NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, t); my_mnl_nlmsg_batch_next(batch); } nftnl_table_free(t); nft_end_batch(batch, false); ipv4_table_setup = false; ipv4_vips_setup = false; ipv6_table_setup = false; ipv6_vips_setup = false; #ifdef _HAVE_VRRP_VMAC_ ipv4_igmp_setup = false; ipv6_igmp_setup = false; #endif setup_ll_ifname = false; setup_ll_ifindex = false; } void nft_end(void) { if (!nl) return; nft_cleanup(); mnl_socket_close(nl); nl = NULL; } keepalived-2.3.3/keepalived/vrrp/Makefile.in0000664000175000017500000006215614772274255014513 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : @VMAC_TRUE@am__append_1 = vrrp_vmac.o @VMAC_TRUE@am__append_2 = vrrp_vmac.c @VRRP_AUTH_TRUE@am__append_3 = vrrp_ipsecah.o @VRRP_AUTH_TRUE@am__append_4 = vrrp_ipsecah.c @WITH_DBUS_TRUE@am__append_5 = vrrp_dbus.o @WITH_DBUS_TRUE@am__append_6 = vrrp_dbus.c @FIREWALL_TRUE@am__append_7 = vrrp_firewall.o vrrp_firewall.o @FIREWALL_TRUE@am__append_8 = vrrp_firewall.c vrrp_firewall.c @IPTABLES_TRUE@am__append_9 = vrrp_iptables.o vrrp_iptables_calls.o @IPTABLES_TRUE@am__append_10 = vrrp_iptables.c vrrp_iptables_calls.c @LIBIPSET_TRUE@am__append_11 = vrrp_ipset.o @LIBIPSET_TRUE@am__append_12 = vrrp_ipset.c @NFTABLES_TRUE@am__append_13 = vrrp_nftables.o @NFTABLES_TRUE@am__append_14 = vrrp_nftables.c @SNMP_VRRP_TRUE@am__append_15 = vrrp_snmp.o @SNMP_VRRP_TRUE@am__append_16 = vrrp_snmp.c @WITH_JSON_TRUE@am__append_17 = vrrp_json.o @WITH_JSON_TRUE@am__append_18 = vrrp_json.c @NETWORK_MANAGER_TRUE@am__append_19 = vrrp_vmac_nm.o @NETWORK_MANAGER_TRUE@am__append_20 = vrrp_vmac_nm.c subdir = keepalived/vrrp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libvrrp_a_AR = $(AR) $(ARFLAGS) libvrrp_a_DEPENDENCIES = $(am__append_1) $(am__append_3) \ $(am__append_5) $(am__append_7) $(am__append_9) \ $(am__append_11) $(am__append_13) $(am__append_15) \ $(am__append_17) $(am__append_19) am_libvrrp_a_OBJECTS = vrrp_daemon.$(OBJEXT) vrrp_print.$(OBJEXT) \ vrrp_data.$(OBJEXT) vrrp_parser.$(OBJEXT) vrrp.$(OBJEXT) \ vrrp_notify.$(OBJEXT) vrrp_scheduler.$(OBJEXT) \ vrrp_sync.$(OBJEXT) vrrp_arp.$(OBJEXT) vrrp_if.$(OBJEXT) \ vrrp_track.$(OBJEXT) vrrp_ipaddress.$(OBJEXT) \ vrrp_ndisc.$(OBJEXT) vrrp_if_config.$(OBJEXT) \ vrrp_static_track.$(OBJEXT) vrrp_iproute.$(OBJEXT) \ vrrp_iprule.$(OBJEXT) vrrp_ip_rule_route_parser.$(OBJEXT) am__EXTRA_libvrrp_a_SOURCES_DIST = vrrp_vmac.c vrrp_ipsecah.c \ vrrp_dbus.c vrrp_firewall.c vrrp_iptables.c \ vrrp_iptables_calls.c vrrp_ipset.c vrrp_nftables.c vrrp_snmp.c \ vrrp_json.c vrrp_vmac_nm.c libvrrp_a_OBJECTS = $(am_libvrrp_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/lib depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/vrrp.Po ./$(DEPDIR)/vrrp_arp.Po \ ./$(DEPDIR)/vrrp_daemon.Po ./$(DEPDIR)/vrrp_data.Po \ ./$(DEPDIR)/vrrp_dbus.Po ./$(DEPDIR)/vrrp_firewall.Po \ ./$(DEPDIR)/vrrp_if.Po ./$(DEPDIR)/vrrp_if_config.Po \ ./$(DEPDIR)/vrrp_ip_rule_route_parser.Po \ ./$(DEPDIR)/vrrp_ipaddress.Po ./$(DEPDIR)/vrrp_iproute.Po \ ./$(DEPDIR)/vrrp_iprule.Po ./$(DEPDIR)/vrrp_ipsecah.Po \ ./$(DEPDIR)/vrrp_ipset.Po ./$(DEPDIR)/vrrp_iptables.Po \ ./$(DEPDIR)/vrrp_iptables_calls.Po ./$(DEPDIR)/vrrp_json.Po \ ./$(DEPDIR)/vrrp_ndisc.Po ./$(DEPDIR)/vrrp_nftables.Po \ ./$(DEPDIR)/vrrp_notify.Po ./$(DEPDIR)/vrrp_parser.Po \ ./$(DEPDIR)/vrrp_print.Po ./$(DEPDIR)/vrrp_scheduler.Po \ ./$(DEPDIR)/vrrp_snmp.Po ./$(DEPDIR)/vrrp_static_track.Po \ ./$(DEPDIR)/vrrp_sync.Po ./$(DEPDIR)/vrrp_track.Po \ ./$(DEPDIR)/vrrp_vmac.Po ./$(DEPDIR)/vrrp_vmac_nm.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libvrrp_a_SOURCES) $(EXTRA_libvrrp_a_SOURCES) DIST_SOURCES = $(libvrrp_a_SOURCES) \ $(am__EXTRA_libvrrp_a_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib \ $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libvrrp.a libvrrp_a_SOURCES = vrrp_daemon.c vrrp_print.c vrrp_data.c \ vrrp_parser.c vrrp.c vrrp_notify.c vrrp_scheduler.c \ vrrp_sync.c vrrp_arp.c vrrp_if.c vrrp_track.c vrrp_ipaddress.c \ vrrp_ndisc.c vrrp_if_config.c vrrp_static_track.c \ vrrp_iproute.c vrrp_iprule.c vrrp_ip_rule_route_parser.c \ ../include/vrrp_daemon.h libvrrp_a_LIBADD = $(am__append_1) $(am__append_3) $(am__append_5) \ $(am__append_7) $(am__append_9) $(am__append_11) \ $(am__append_13) $(am__append_15) $(am__append_17) \ $(am__append_19) EXTRA_libvrrp_a_SOURCES = $(am__append_2) $(am__append_4) \ $(am__append_6) $(am__append_8) $(am__append_10) \ $(am__append_12) $(am__append_14) $(am__append_16) \ $(am__append_18) $(am__append_20) all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/vrrp/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/vrrp/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libvrrp.a: $(libvrrp_a_OBJECTS) $(libvrrp_a_DEPENDENCIES) $(EXTRA_libvrrp_a_DEPENDENCIES) $(AM_V_at)-rm -f libvrrp.a $(AM_V_AR)$(libvrrp_a_AR) libvrrp.a $(libvrrp_a_OBJECTS) $(libvrrp_a_LIBADD) $(AM_V_at)$(RANLIB) libvrrp.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_arp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_daemon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_data.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_dbus.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_firewall.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_if.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_if_config.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_ip_rule_route_parser.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_ipaddress.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_iproute.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_iprule.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_ipsecah.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_ipset.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_iptables.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_iptables_calls.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_json.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_ndisc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_nftables.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_notify.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_parser.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_print.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_scheduler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_snmp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_static_track.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_sync.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_track.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_vmac.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vrrp_vmac_nm.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/vrrp.Po -rm -f ./$(DEPDIR)/vrrp_arp.Po -rm -f ./$(DEPDIR)/vrrp_daemon.Po -rm -f ./$(DEPDIR)/vrrp_data.Po -rm -f ./$(DEPDIR)/vrrp_dbus.Po -rm -f ./$(DEPDIR)/vrrp_firewall.Po -rm -f ./$(DEPDIR)/vrrp_if.Po -rm -f ./$(DEPDIR)/vrrp_if_config.Po -rm -f ./$(DEPDIR)/vrrp_ip_rule_route_parser.Po -rm -f ./$(DEPDIR)/vrrp_ipaddress.Po -rm -f ./$(DEPDIR)/vrrp_iproute.Po -rm -f ./$(DEPDIR)/vrrp_iprule.Po -rm -f ./$(DEPDIR)/vrrp_ipsecah.Po -rm -f ./$(DEPDIR)/vrrp_ipset.Po -rm -f ./$(DEPDIR)/vrrp_iptables.Po -rm -f ./$(DEPDIR)/vrrp_iptables_calls.Po -rm -f ./$(DEPDIR)/vrrp_json.Po -rm -f ./$(DEPDIR)/vrrp_ndisc.Po -rm -f ./$(DEPDIR)/vrrp_nftables.Po -rm -f ./$(DEPDIR)/vrrp_notify.Po -rm -f ./$(DEPDIR)/vrrp_parser.Po -rm -f ./$(DEPDIR)/vrrp_print.Po -rm -f ./$(DEPDIR)/vrrp_scheduler.Po -rm -f ./$(DEPDIR)/vrrp_snmp.Po -rm -f ./$(DEPDIR)/vrrp_static_track.Po -rm -f ./$(DEPDIR)/vrrp_sync.Po -rm -f ./$(DEPDIR)/vrrp_track.Po -rm -f ./$(DEPDIR)/vrrp_vmac.Po -rm -f ./$(DEPDIR)/vrrp_vmac_nm.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/vrrp.Po -rm -f ./$(DEPDIR)/vrrp_arp.Po -rm -f ./$(DEPDIR)/vrrp_daemon.Po -rm -f ./$(DEPDIR)/vrrp_data.Po -rm -f ./$(DEPDIR)/vrrp_dbus.Po -rm -f ./$(DEPDIR)/vrrp_firewall.Po -rm -f ./$(DEPDIR)/vrrp_if.Po -rm -f ./$(DEPDIR)/vrrp_if_config.Po -rm -f ./$(DEPDIR)/vrrp_ip_rule_route_parser.Po -rm -f ./$(DEPDIR)/vrrp_ipaddress.Po -rm -f ./$(DEPDIR)/vrrp_iproute.Po -rm -f ./$(DEPDIR)/vrrp_iprule.Po -rm -f ./$(DEPDIR)/vrrp_ipsecah.Po -rm -f ./$(DEPDIR)/vrrp_ipset.Po -rm -f ./$(DEPDIR)/vrrp_iptables.Po -rm -f ./$(DEPDIR)/vrrp_iptables_calls.Po -rm -f ./$(DEPDIR)/vrrp_json.Po -rm -f ./$(DEPDIR)/vrrp_ndisc.Po -rm -f ./$(DEPDIR)/vrrp_nftables.Po -rm -f ./$(DEPDIR)/vrrp_notify.Po -rm -f ./$(DEPDIR)/vrrp_parser.Po -rm -f ./$(DEPDIR)/vrrp_print.Po -rm -f ./$(DEPDIR)/vrrp_scheduler.Po -rm -f ./$(DEPDIR)/vrrp_snmp.Po -rm -f ./$(DEPDIR)/vrrp_static_track.Po -rm -f ./$(DEPDIR)/vrrp_sync.Po -rm -f ./$(DEPDIR)/vrrp_track.Po -rm -f ./$(DEPDIR)/vrrp_vmac.Po -rm -f ./$(DEPDIR)/vrrp_vmac_nm.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-generic clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/vrrp/vrrp_track.c0000664000175000017500000006341314770002501014741 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Interface tracking framework. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include #include #include #include #include /* local include */ #include "vrrp_track.h" #include "vrrp_data.h" #include "vrrp.h" #include "vrrp_sync.h" #include "logger.h" #include "memory.h" #include "vrrp_scheduler.h" #include "scheduler.h" #include "parser.h" #include "utils.h" #include "vrrp_notify.h" #include "bitops.h" #include "track_file.h" #include "main.h" #ifdef _WITH_TRACK_PROCESS_ #include "track_process.h" #endif /* Track interface dump */ static void dump_track_if(FILE *fp, const tracked_if_t *tip) { conf_write(fp, " %s weight %d%s", IF_NAME(tip->ifp), tip->weight, tip->weight_reverse ? " reverse" : ""); } void dump_track_if_list(FILE *fp, const list_head_t *l) { tracked_if_t *tip; list_for_each_entry(tip, l, e_list) dump_track_if(fp, tip); } void free_track_if(tracked_if_t *tip) { list_del_init(&tip->e_list); FREE(tip); } void free_track_if_list(list_head_t *l) { tracked_if_t *tip, *tip_tmp; list_for_each_entry_safe(tip, tip_tmp, l, e_list) free_track_if(tip); } void alloc_track_if(const char *name, list_head_t *l, const vector_t *strvec) { interface_t *ifp; tracked_if_t *tip; int weight = 0; const char *tracked = strvec_slot(strvec, 0); bool reverse = false; ifp = if_get_by_ifname(tracked, IF_CREATE_IF_DYNAMIC); if (!ifp) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) tracked interface %s doesn't exist" , name, tracked); return; } /* Check this vrrp isn't already tracking the i/f */ list_for_each_entry(tip, l, e_list) { if (tip->ifp == ifp) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_interface %s - ignoring" , name, tracked); return; } } if (vector_size(strvec) >= 2) { if (strcmp(strvec_slot(strvec, 1), "weight")) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track_interface %s" " option %s - ignoring" , name, tracked, strvec_slot(strvec, 1)); return; } if (vector_size(strvec) == 2) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified" " for track_interface %s - ignoring" , name, tracked); return; } if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight %s for %s must be" " between [-253..253] inclusive. Ignoring..." , name, strvec_slot(strvec, 2), tracked); weight = 0; } else if (weight == -254 || weight == 254) { /* This check can be removed once users have migrated away from +/-254 */ report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for %s cannot be +/-254." " Setting to +/-253" , name, tracked); weight = weight == -254 ? -253 : 253; } if (vector_size(strvec) >= 4) { if (!strcmp(strvec_slot(strvec, 3), "reverse")) reverse = true; else report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track_interace %s" " weight option %s - ignoring" , name, tracked, strvec_slot(strvec, 3)); } } PMALLOC(tip); INIT_LIST_HEAD(&tip->e_list); tip->ifp = ifp; tip->weight = weight; tip->weight_reverse = reverse; list_add_tail(&tip->e_list, l); } vrrp_script_t * __attribute__ ((pure)) find_script_by_name(const char *name) { vrrp_script_t *scr; list_for_each_entry(scr, &vrrp_data->vrrp_script, e_list) { if (!strcmp(scr->sname, name)) return scr; } return NULL; } /* Track script dump */ static void dump_track_script(FILE *fp, const tracked_sc_t *tsc) { conf_write(fp, " %s weight %d%s", tsc->scr->sname, tsc->weight, tsc->weight_reverse ? " reverse" : ""); } void dump_track_script_list(FILE *fp, const list_head_t *l) { tracked_sc_t *tsc; list_for_each_entry(tsc, l, e_list) dump_track_script(fp, tsc); } void free_track_script(tracked_sc_t *tsc) { list_del_init(&tsc->e_list); FREE(tsc); } void free_track_script_list(list_head_t *l) { tracked_sc_t *tsc, *tsc_tmp; list_for_each_entry_safe(tsc, tsc_tmp, l, e_list) free_track_script(tsc); } void alloc_track_script(const char *name, list_head_t *l, const vector_t *strvec) { vrrp_script_t *vsc; tracked_sc_t *tsc; int weight; const char *tracked = strvec_slot(strvec, 0); tracked_sc_t *etsc; bool reverse; vsc = find_script_by_name(tracked); /* Ignoring if no script found */ if (!vsc) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) track script %s not found, ignoring..." , name, tracked); return; } /* Check this vrrp isn't already tracking the script */ list_for_each_entry(etsc, l, e_list) { if (etsc->scr == vsc) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_script %s - ignoring" , name, tracked); return; } } /* default weight */ weight = vsc->weight; reverse = vsc->weight_reverse; if (vector_size(strvec) >= 2) { if (strcmp(strvec_slot(strvec, 1), "weight")) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track script option %s - ignoring" , name, strvec_slot(strvec, 1)); return; } if (vector_size(strvec) == 2) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified for" " track script %s - ignoring" , name, tracked); return; } if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) { weight = vsc->weight; report_config_error(CONFIG_GENERAL_ERROR, "(%s) track script %s: weight must be" " between [-253..253] inclusive, ignoring..." , name, tracked); } else if (weight == -254 || weight == 254) { /* This check can be removed once users have migrated away from +/-254 */ report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for %s cannot be +/-254." " Setting to +/-253" , name, tracked); weight = weight == -254 ? -253 : 253; } if (vector_size(strvec) >= 4) { if (!strcmp(strvec_slot(strvec, 3), "reverse")) reverse = true; else if (!strcmp(strvec_slot(strvec, 3), "noreverse")) reverse = false; else report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track_script %s" " weight option %s - ignoring" , name, tracked, strvec_slot(strvec, 3)); } } PMALLOC(tsc); INIT_LIST_HEAD(&tsc->e_list); tsc->scr = vsc; tsc->weight = weight; tsc->weight_reverse = reverse; vsc->init_state = reload ? SCRIPT_INIT_STATE_INIT_RELOAD : SCRIPT_INIT_STATE_INIT; list_add_tail(&tsc->e_list, l); } #ifdef _WITH_TRACK_PROCESS_ static vrrp_tracked_process_t * __attribute__ ((pure)) find_tracked_process_by_name(const char *name) { vrrp_tracked_process_t *process; list_for_each_entry(process, &vrrp_data->vrrp_track_processes, e_list) { if (!strcmp(process->pname, name)) return process; } return NULL; } /* Track process dump */ static void dump_track_process(FILE *fp, const tracked_process_t *tprocess) { conf_write(fp, " %s, weight %d%s", tprocess->process->pname , tprocess->weight, tprocess->weight_reverse ? " reverse" : ""); } void dump_track_process_list(FILE *fp, const list_head_t *l) { tracked_process_t *tprocess; list_for_each_entry(tprocess, l, e_list) dump_track_process(fp, tprocess); } void free_track_process_list(list_head_t *l) { tracked_process_t *tprocess, *tprocess_tmp; list_for_each_entry_safe(tprocess, tprocess_tmp, l, e_list) FREE(tprocess); } void alloc_track_process(const char *name, list_head_t *l, const vector_t *strvec) { vrrp_tracked_process_t *vsp; const char *tracked = strvec_slot(strvec, 0); tracked_process_t *tprocess; int weight; bool reverse; vsp = find_tracked_process_by_name(tracked); /* Ignoring if no process found */ if (!vsp) { if (proc_events_not_supported) report_config_error(CONFIG_GENERAL_ERROR, "(%s) track process not supported by kernel" , name); else report_config_error(CONFIG_GENERAL_ERROR, "(%s) track process %s not found, ignoring..." , name, tracked); return; } /* Check this vrrp isn't already tracking the process */ list_for_each_entry(tprocess, l, e_list) { if (tprocess->process == vsp) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_process %s - ignoring" , name, tracked); return; } } weight = vsp->weight; reverse = vsp->weight_reverse; if (vector_size(strvec) >= 2) { if (strcmp(strvec_slot(strvec, 1), "weight")) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track process option %s - ignoring" , name, strvec_slot(strvec, 1)); return; } if (vector_size(strvec) == 2) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified for" " track process %s - ignoring" , name, tracked); return; } if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for track process %s must be in " "[-254..254] inclusive. Ignoring..." , name, tracked); weight = vsp->weight; } if (vector_size(strvec) >= 4) { if (!strcmp(strvec_slot(strvec, 3), "reverse")) reverse = true; else if (!strcmp(strvec_slot(strvec, 3), "noreverse")) reverse = false; else report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track_process %s weight" " option %s - ignoring" , name, tracked, strvec_slot(strvec, 3)); } } PMALLOC(tprocess); INIT_LIST_HEAD(&tprocess->e_list); tprocess->process = vsp; tprocess->weight = weight; tprocess->weight_reverse = reverse; list_add_tail(&tprocess->e_list, l); } #endif #ifdef _WITH_BFD_ /* VRRP Track bfd related */ vrrp_tracked_bfd_t * __attribute__ ((pure)) find_vrrp_tracked_bfd_by_name(const char *name) { vrrp_tracked_bfd_t *bfd; list_for_each_entry(bfd, &vrrp_data->vrrp_track_bfds, e_list) { if (!strcmp(bfd->bname, name)) return bfd; } return NULL; } vrrp_tracked_bfd_t * alloc_vrrp_tracked_bfd(const char *name, list_head_t *l) { vrrp_tracked_bfd_t *tbfd; if (strlen(name) >= BFD_INAME_MAX) { report_config_error(CONFIG_GENERAL_ERROR, "BFD name %s too long", name); skip_block(true); return NULL; } list_for_each_entry(tbfd, l, e_list) { if (!strcmp(name, tbfd->bname)) { report_config_error(CONFIG_GENERAL_ERROR, "BFD %s already specified", name); skip_block(true); return NULL; } } PMALLOC(tbfd); INIT_LIST_HEAD(&tbfd->e_list); strncpy(tbfd->bname, name, BFD_INAME_MAX-1); /* Not really need, but... */ tbfd->weight = 0; tbfd->weight_reverse = false; tbfd->bfd_up = false; INIT_LIST_HEAD(&tbfd->tracking_vrrp); return tbfd; } /* Track bfd related */ static void dump_tracked_bfd(FILE *fp, const tracked_bfd_t *tbfd) { conf_write(fp, " %s: weight %d%s", tbfd->bfd->bname, tbfd->weight, tbfd->weight_reverse ? " reverse" : ""); } void dump_tracked_bfd_list(FILE *fp, const list_head_t *l) { tracked_bfd_t *tbfd; list_for_each_entry(tbfd, l, e_list) dump_tracked_bfd(fp, tbfd); } void free_track_bfd(tracked_bfd_t *tbfd) { list_del_init(&tbfd->e_list); FREE(tbfd); } void free_track_bfd_list(list_head_t *l) { tracked_bfd_t *tbfd, *tbfd_tmp; list_for_each_entry_safe(tbfd, tbfd_tmp, l, e_list) free_track_bfd(tbfd); } void alloc_track_bfd(const char *name, list_head_t *l, const vector_t *strvec) { vrrp_tracked_bfd_t *vtb; tracked_bfd_t *tbfd; const char *tracked = strvec_slot(strvec, 0); tracked_bfd_t *etbfd; int weight; bool reverse = false; vtb = find_vrrp_tracked_bfd_by_name(tracked); /* Ignoring if no bfd found */ if (!vtb) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) track bfd %s not found, ignoring..." , name, tracked); return; } /* Check this vrrp isn't already tracking the bfd */ list_for_each_entry(etbfd, l, e_list) { if (etbfd->bfd == vtb) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_bfd %s - ignoring" , name, tracked); return; } } weight = vtb->weight; reverse = vtb->weight_reverse; if (vector_size(strvec) >= 2) { if (strcmp(strvec_slot(strvec, 1), "weight")) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track bfd %s option %s - ignoring" , name, tracked, strvec_slot(strvec, 1)); return; } if (vector_size(strvec) == 2) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified" " for track bfd %s - ignoring" , name, tracked); return; } if (!read_int_strvec(strvec, 2, &weight, -253, 253, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for track bfd %s must be in " "[-253..253] inclusive. Ignoring..." , name, tracked); weight = vtb->weight; } if (vector_size(strvec) >= 4) { if (!strcmp(strvec_slot(strvec, 3), "reverse")) reverse = true; else if (!strcmp(strvec_slot(strvec, 3), "noreverse")) reverse = false; else { report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track bfd %s weight" " option %s - ignoring" , name, tracked, strvec_slot(strvec, 3)); return; } } } PMALLOC(tbfd); INIT_LIST_HEAD(&tbfd->e_list); tbfd->bfd = vtb; tbfd->weight = weight; tbfd->weight_reverse = reverse; list_add_tail(&tbfd->e_list, l); } #endif static void set_fault(vrrp_t *vrrp, unsigned flag) { #ifdef _FAULT_FLAGS_CHECK_ if (flag != VRRP_FAULT_FL_TRACKER && __test_bit(flag, &vrrp->flags_if_fault)) log_message(LOG_INFO, "(%s) BUG - down_instance flag %u already set in 0x%lx", vrrp->iname, flag, vrrp->flags_if_fault); if (!__test_bit(VRRP_FAULT_FL_TRACKER, &vrrp->flags_if_fault) != !vrrp->num_track_fault) log_message(LOG_INFO, "(%s) BUG - set_fault - tracker flag 0x%lx does not match num_track_fault %u", vrrp->iname, vrrp->flags_if_fault, vrrp->num_track_fault); #endif __set_bit(flag, &vrrp->flags_if_fault); if (flag == VRRP_FAULT_FL_TRACKER) vrrp->num_track_fault++; } void down_instance(vrrp_t *vrrp, unsigned down_flag) // last param should be vrrp_fault_fl_t down_flag { bool already_down = vrrp->flags_if_fault; /* We can not use down_instance() for several down reasons * at the same time */ set_fault(vrrp, down_flag); if (!already_down || vrrp->state == VRRP_STATE_INIT) { vrrp->wantstate = VRRP_STATE_FAULT; if (vrrp->state == VRRP_STATE_MAST) vrrp_state_leave_master(vrrp, true); else vrrp_state_leave_fault(vrrp); if (vrrp->sync && vrrp->sync->num_member_fault++ == 0) vrrp_sync_fault(vrrp); } } /* Set effective priorty, issue message on changes */ void vrrp_set_effective_priority(vrrp_t *vrrp) { uint8_t new_prio; uint32_t old_down_timer; /* Don't change priority if address owner */ if (vrrp->base_priority == VRRP_PRIO_OWNER) return; if (vrrp->total_priority < 1) new_prio = 1; else if (vrrp->total_priority >= VRRP_PRIO_OWNER) new_prio = VRRP_PRIO_OWNER - 1; else new_prio = (uint8_t)vrrp->total_priority; if (vrrp->effective_priority == new_prio) return; log_message(LOG_INFO, "(%s) Changing effective priority from %d to %d", vrrp->iname, vrrp->effective_priority, new_prio); vrrp->effective_priority = new_prio; old_down_timer = vrrp->ms_down_timer; vrrp->ms_down_timer = VRRP_MS_DOWN_TIMER(vrrp); if (vrrp->state == VRRP_STATE_BACK) { if (old_down_timer < vrrp->ms_down_timer) vrrp->sands = timer_add_long(vrrp->sands, vrrp->ms_down_timer - old_down_timer); else vrrp->sands = timer_sub_long(vrrp->sands, old_down_timer - vrrp->ms_down_timer); vrrp_thread_requeue_read(vrrp); } if (vrrp->notify_priority_changes) send_instance_priority_notifies(vrrp); } static void process_script_update_priority(int weight, int multiplier, vrrp_script_t *vscript, bool script_ok, vrrp_t *vrrp) { bool instance_left_init = false; if (!weight) { if (vscript->init_state == SCRIPT_INIT_STATE_INIT) { /* We need to adjust the number of scripts in init state */ if (!--vrrp->num_script_init) { instance_left_init = true; if (vrrp->sync) vrrp->sync->num_member_init--; } } if (script_ok != (multiplier == 1)) { /* The instance needs to go down */ down_instance(vrrp, VRRP_FAULT_FL_TRACKER); } else if (!vrrp->num_script_init && vscript->init_state != SCRIPT_INIT_STATE_INIT_RELOAD && (!vrrp->sync || !vrrp->sync->num_member_init)) { /* The instance can come up * Set want_state = BACKUP/MASTER, and check i/fs and sync groups */ try_up_instance(vrrp, instance_left_init, VRRP_FAULT_FL_TRACKER); } return; } if (vscript->init_state == SCRIPT_INIT_STATE_INIT || vscript->init_state == SCRIPT_INIT_STATE_INIT_RELOAD) { /* If the script hasn't previously exited, we need to only adjust the priority if the state the script is now in causes an adjustment to the priority */ if (script_ok) { if (weight > 0) vrrp->total_priority += weight * multiplier; } else { if (weight < 0) vrrp->total_priority += weight * multiplier; } } else { if (script_ok) vrrp->total_priority += abs(weight) * multiplier; else vrrp->total_priority -= abs(weight) * multiplier; } vrrp_set_effective_priority(vrrp); } void update_script_priorities(vrrp_script_t *vscript, bool script_ok) { tracking_obj_t* top; vrrp_t *vrrp; /* First process the vrrp instances tracking the script */ list_for_each_entry(top, &vscript->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; process_script_update_priority(top->weight, top->weight_multiplier, vscript, script_ok, vrrp); } } static void initialise_track_script_state(tracked_sc_t *tsc, vrrp_t *vrrp) { if (!tsc->weight) { if (tsc->scr->init_state == SCRIPT_INIT_STATE_INIT) vrrp->num_script_init++; else if (tsc->scr->init_state == SCRIPT_INIT_STATE_FAILED || (tsc->scr->init_state != SCRIPT_INIT_STATE_INIT_RELOAD && (tsc->scr->result >= 0 && tsc->scr->result < tsc->scr->rise))) { /* The script is in fault state */ set_fault(vrrp, VRRP_FAULT_FL_TRACKER); log_message(LOG_INFO, "(%s): entering FAULT state due to script %s", vrrp->iname, tsc->scr->sname); vrrp->state = VRRP_STATE_FAULT; } return; } /* Don't change effective priority if address owner */ if (vrrp->base_priority == VRRP_PRIO_OWNER) return; if (tsc->scr->init_state != SCRIPT_INIT_STATE_INIT && tsc->scr->init_state != SCRIPT_INIT_STATE_INIT_RELOAD) { if (tsc->scr->result >= tsc->scr->rise) { if (tsc->weight > 0) vrrp->total_priority += tsc->weight; } else { if (tsc->weight < 0) vrrp->total_priority += tsc->weight; } } } #ifdef _WITH_BFD_ static void initialise_track_bfd_state(tracked_bfd_t *tbfd, vrrp_t *vrrp) { int multiplier = tbfd->weight_reverse ? -1 : 1; if (tbfd->weight) { if (tbfd->bfd->bfd_up) { if (tbfd->weight > 0) vrrp->total_priority += tbfd->weight * multiplier; } else { if (tbfd->weight < 0) vrrp->total_priority += tbfd->weight * multiplier; else if (!tbfd->weight) { set_fault(vrrp, VRRP_FAULT_FL_TRACKER); vrrp->state = VRRP_STATE_FAULT; } } } else if (tbfd->bfd->bfd_up == tbfd->weight_reverse) { set_fault(vrrp, VRRP_FAULT_FL_TRACKER); vrrp->state = VRRP_STATE_FAULT; } } #endif static void initialise_interface_tracking_priorities(void) { tracking_obj_t *top; vrrp_t *vrrp; interface_t *ifp; list_head_t *ifq; ifq = get_interface_queue(); list_for_each_entry(ifp, ifq, e_list) { list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (top->weight == VRRP_NOT_TRACK_IF) continue; if (!top->weight) { if (IF_FLAGS_UP(ifp) != (top->weight_multiplier == 1)) { /* The instance is down */ log_message(LOG_INFO, "(%s): entering FAULT state (interface %s down)", vrrp->iname, ifp->ifname); vrrp->state = VRRP_STATE_FAULT; #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && VRRP_CONFIGURED_IFP(vrrp) == ifp) __set_bit(VRRP_FAULT_FL_BASE_INTERFACE_DOWN, &vrrp->flags_if_fault); else #endif /* assuming there is only one tracked interface per vrrp : to be checked */ __set_bit(VRRP_FAULT_FL_INTERFACE_DOWN, &vrrp->flags_if_fault); } } else if (IF_FLAGS_UP(ifp)) { if (top->weight > 0) vrrp->total_priority += top->weight * top->weight_multiplier; } else { if (top->weight < 0) vrrp->total_priority += top->weight * top->weight_multiplier; } } } } static void initialise_vrrp_file_tracking_priorities(void) { tracked_file_t *tfile; tracking_obj_t *top; vrrp_t *vrrp; int status; list_for_each_entry(tfile, &vrrp_data->vrrp_track_files, e_list) { list_for_each_entry(top, &tfile->tracking_obj, e_list) { vrrp = top->obj.vrrp; status = !top->weight ? (!!tfile->last_status == (top->weight_multiplier == 1) ? -254 : 0 ) : tfile->last_status * top->weight * top->weight_multiplier; if (status <= -254) { /* The instance is down */ log_message(LOG_INFO, "(%s): entering FAULT state (tracked file %s has status %i)", vrrp->iname, tfile->fname, status); vrrp->state = VRRP_STATE_FAULT; set_fault(vrrp, VRRP_FAULT_FL_TRACKER); } else vrrp->total_priority += (status > 253 ? 253 : status); } } } #ifdef _WITH_TRACK_PROCESS_ static void initialise_process_tracking_priorities(void) { vrrp_tracked_process_t *tprocess; tracking_obj_t *top; vrrp_t *vrrp; list_for_each_entry(tprocess, &vrrp_data->vrrp_track_processes, e_list) { tprocess->have_quorum = (tprocess->num_cur_proc >= tprocess->quorum && tprocess->num_cur_proc <= tprocess->quorum_max); list_for_each_entry(top, &tprocess->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (!top->weight) { if (tprocess->have_quorum != (top->weight_multiplier == 1)) { /* The instance is down */ log_message(LOG_INFO, "(%s) entering FAULT state (tracked process %s" " quorum not achieved)" , vrrp->iname, tprocess->pname); set_fault(vrrp, VRRP_FAULT_FL_TRACKER); vrrp->state = VRRP_STATE_FAULT; } } else if (tprocess->have_quorum) { if (top->weight > 0) vrrp->total_priority += top->weight * top->weight_multiplier; } else { if (top->weight < 0) vrrp->total_priority += top->weight * top->weight_multiplier; } } } } #endif static void initialise_vrrp_tracking_priorities(vrrp_t *vrrp) { tracked_sc_t *tsc; #ifdef _WITH_BFD_ tracked_bfd_t *tbfd; #endif /* If no src address has been specified, and the interface doesn't have * an appropriate address, put the interface into fault state */ if (vrrp->saddr.ss_family == AF_UNSPEC) { /* The instance is down */ log_message(LOG_INFO, "(%s) entering FAULT state (no IPv%d address for interface)" , vrrp->iname, vrrp->family == AF_INET ? 4 : 6); vrrp->state = VRRP_STATE_FAULT; __set_bit(VRRP_FAULT_FL_NO_SOURCE_IP, &vrrp->flags_if_fault); } /* Initialise the vrrp instance's tracked scripts */ list_for_each_entry(tsc, &vrrp->track_script, e_list) initialise_track_script_state(tsc, vrrp); #ifdef _WITH_BFD_ /* Initialise the vrrp instance's tracked BFDs */ list_for_each_entry(tbfd, &vrrp->track_bfd, e_list) initialise_track_bfd_state(tbfd, vrrp); #endif /* If have a sync group, initialise it's tracked scripts and bfds */ if (vrrp->sync) { list_for_each_entry(tsc, &vrrp->sync->track_script, e_list) initialise_track_script_state(tsc, vrrp); #ifdef _WITH_BFD_ list_for_each_entry(tbfd, &vrrp->sync->track_bfd, e_list) initialise_track_bfd_state(tbfd, vrrp); #endif } vrrp_set_effective_priority(vrrp); } void initialise_tracking_priorities(void) { vrrp_t *vrrp; /* Check for instance down due to an interface */ initialise_interface_tracking_priorities(); initialise_vrrp_file_tracking_priorities(); #ifdef _WITH_TRACK_PROCESS_ initialise_process_tracking_priorities(); #endif /* Now check for tracking scripts, files, bfd etc. */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { /* Set effective priority and fault state */ initialise_vrrp_tracking_priorities(vrrp); if (vrrp->sync) { if (vrrp->state == VRRP_STATE_FAULT) { if (vrrp->sync->state != VRRP_STATE_FAULT) { vrrp->sync->state = VRRP_STATE_FAULT; log_message(LOG_INFO, "VRRP_Group(%s): Syncing %s to FAULT state" , vrrp->sync->gname, vrrp->iname); } vrrp->sync->num_member_fault++; } if (vrrp->num_script_init) { /* Update init count on sync group if needed */ vrrp->sync->num_member_init++; if (vrrp->sync->state != VRRP_STATE_FAULT) vrrp->sync->state = VRRP_STATE_INIT; } } } } #ifdef _WITH_TRACK_PROCESS_ void process_update_track_process_status(vrrp_tracked_process_t *tprocess, bool now_up) { tracking_obj_t *top; vrrp_t *vrrp; log_message(LOG_INFO, "Quorum %s for tracked process %s", now_up ? "gained" : "lost", tprocess->pname); list_for_each_entry(top, &tprocess->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (!top->weight) { if (now_up == (top->weight_multiplier == 1)) try_up_instance(vrrp, false, VRRP_FAULT_FL_TRACKER); else down_instance(vrrp, VRRP_FAULT_FL_TRACKER); } else if (vrrp->base_priority != VRRP_PRIO_OWNER) { if ((top->weight > 0) == now_up) vrrp->total_priority += top->weight * top->weight_multiplier; else vrrp->total_priority -= top->weight * top->weight_multiplier; vrrp_set_effective_priority(vrrp); } } } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_data.c0000664000175000017500000013463414770002666014566 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Dynamic data structure definition. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include "utils.h" #include "logger.h" #include "bitops.h" #include "rttables.h" #include "global_data.h" #include "main.h" #include "vrrp_data.h" #include "vrrp_sync.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_vmac.h" #endif #include "vrrp_ipaddress.h" #include "vrrp_iprule.h" #include "vrrp_iproute.h" #include "vrrp_track.h" #include "vrrp_sock.h" #ifdef _WITH_SNMP_RFCV3_ #include "vrrp_snmp.h" #endif #include "vrrp_static_track.h" #include "parser.h" #include "track_file.h" #include "vrrp_parser.h" /* global vars */ vrrp_data_t *vrrp_data = NULL; vrrp_data_t *old_vrrp_data = NULL; void *vrrp_buffer; size_t vrrp_buffer_len; static const char * get_state_str(int state) { if (state == VRRP_STATE_INIT) return "INIT"; if (state == VRRP_STATE_BACK) return "BACKUP"; if (state == VRRP_STATE_MAST) return "MASTER"; if (state == VRRP_STATE_FAULT) return "FAULT"; return "unknown"; } /* Static track groups facility function */ static void free_static_track_groups_list(list_head_t *l) { static_track_group_t *tgroup, *tgroup_tmp; list_for_each_entry_safe(tgroup, tgroup_tmp, l, e_list) free_static_track_group(tgroup); } static void dump_static_track_groups_list(FILE *fp, const list_head_t *l) { static_track_group_t *tgroup; list_for_each_entry(tgroup, l, e_list) dump_static_track_group(fp, tgroup); } static_track_group_t * alloc_static_track_group(const char *gname) { static_track_group_t *new; /* Allocate new VRRP group structure */ PMALLOC(new); INIT_LIST_HEAD(&new->e_list); INIT_LIST_HEAD(&new->vrrp_instances); new->gname = STRDUP(gname); return new; } /* Static addresses facility function */ void alloc_saddress(const vector_t *strvec) { ip_address_t *new_ipaddr; if ((new_ipaddr = alloc_ipaddress(strvec, true))) list_add_tail(&new_ipaddr->e_list, &vrrp_data->static_addresses); } /* Static routes facility function */ void alloc_sroute(const vector_t *strvec) { alloc_route(&vrrp_data->static_routes, strvec, true); } /* Static rules facility function */ void alloc_srule(const vector_t *strvec) { alloc_rule(&vrrp_data->static_rules, strvec, true); } /* VRRP Reference list functions */ static void free_vrrp_sync_group_list(list_head_t *l) { vrrp_t *vrrp, *vrrp_tmp; /* Remove the vrrp instances from the sync group */ list_for_each_entry_safe(vrrp, vrrp_tmp, l, s_list) { vrrp->sync = NULL; list_del_init(&vrrp->s_list); } } static void dump_vrrp_sync_group_list(FILE *fp, const list_head_t *l) { vrrp_t *vrrp; list_for_each_entry(vrrp, l, s_list) conf_write(fp, " %s", vrrp->iname); } /* VRRP facility functions */ void free_sync_group(vrrp_sgroup_t *sgroup) { list_del_init(&sgroup->e_list); if (sgroup->iname) { /* If we are terminating at init time, sgroup->vrrp_instances may not be initialised * yet, or it may have only one member, in which case sgroup->iname will still be set */ if (sgroup->vrrp_instances.prev != sgroup->vrrp_instances.next) log_message(LOG_INFO, "sync group %s - iname vector exists when freeing group" , sgroup->gname); free_strvec(sgroup->iname); } FREE_CONST(sgroup->gname); free_vrrp_sync_group_list(&sgroup->vrrp_instances); free_track_if_list(&sgroup->track_ifp); free_track_script_list(&sgroup->track_script); free_track_file_monitor_list(&sgroup->track_file); #ifdef _WITH_TRACK_PROCESS_ free_track_process_list(&sgroup->track_process); #endif #ifdef _WITH_BFD_ free_track_bfd_list(&sgroup->track_bfd); #endif free_notify_script(&sgroup->script_backup); free_notify_script(&sgroup->script_master); free_notify_script(&sgroup->script_fault); free_notify_script(&sgroup->script_stop); free_notify_script(&sgroup->script); FREE(sgroup); } static void free_sync_group_list(list_head_t *l) { vrrp_sgroup_t *sgroup, *sgroup_tmp; list_for_each_entry_safe(sgroup, sgroup_tmp, l, e_list) free_sync_group(sgroup); } static void dump_notify_script(FILE *fp, const notify_script_t *script, const char *type) { if (!script) return; if (script->path) conf_write(fp, " %s state transition script = %s, params = %s, uid:gid %u:%u" , type, script->path, cmd_str(script), script->uid, script->gid); else conf_write(fp, " %s state transition script = %s, uid:gid %u:%u" , type, cmd_str(script), script->uid, script->gid); } static void dump_sync_group(FILE *fp, const vrrp_sgroup_t *sgroup) { conf_write(fp, " VRRP Sync Group = %s, %s", sgroup->gname, get_state_str(sgroup->state)); conf_write(fp, " Num member fault %u, num member init %u", sgroup->num_member_fault, sgroup->num_member_init); if (!list_empty(&sgroup->vrrp_instances)) { conf_write(fp, " VRRP member instances :"); dump_vrrp_sync_group_list(fp, &sgroup->vrrp_instances); } if (sgroup->sgroup_tracking_weight) conf_write(fp, " sync group tracking weight set"); conf_write(fp, " Using smtp notification = %s", sgroup->smtp_alert ? "yes" : "no"); if (sgroup->notify_priority_changes != -1) conf_write(fp, " Notify priority changes = %s", sgroup->notify_priority_changes ? "yes" : "no"); if (!list_empty(&sgroup->track_ifp)) { conf_write(fp, " Tracked interfaces :"); dump_track_if_list(fp, &sgroup->track_ifp); } if (!list_empty(&sgroup->track_script)) { conf_write(fp, " Tracked scripts :"); dump_track_script_list(fp, &sgroup->track_script); } if (!list_empty(&sgroup->track_file)) { conf_write(fp, " Tracked files :"); dump_track_file_monitor_list(fp, &sgroup->track_file); } #ifdef _WITH_TRACK_PROCESS_ if (!list_empty(&sgroup->track_process)) { conf_write(fp, " Tracked process :"); dump_track_process_list(fp, &sgroup->track_process); } #endif #ifdef _WITH_BFD_ if (!list_empty(&sgroup->track_bfd)) { conf_write(fp, " Tracked BFDs :"); dump_tracked_bfd_list(fp, &sgroup->track_bfd); } #endif dump_notify_script(fp, sgroup->script_backup, "Backup"); dump_notify_script(fp, sgroup->script_master, "Master"); dump_notify_script(fp, sgroup->script_fault, "Fault"); dump_notify_script(fp, sgroup->script_stop, "Stop"); dump_notify_script(fp, sgroup->script, "Generic"); } static void dump_sync_group_list(FILE *fp, const list_head_t *l) { vrrp_sgroup_t *sgroup; list_for_each_entry(sgroup, l, e_list) dump_sync_group(fp, sgroup); } void dump_tracking_vrrp(FILE *fp, const void *obj) { const tracking_obj_t *top = obj; const vrrp_t *vrrp = top->obj.vrrp; conf_write(fp, " %s, weight %d%s%s" , vrrp->iname, top->weight , top->weight_multiplier == -1 ? " reverse" : "" , top->type & TRACK_VRRP_DYNAMIC ? " (dynamic)" : ""); } void dump_tracking_vrrp_list(FILE *fp, const list_head_t *l) { tracking_obj_t *top; list_for_each_entry(top, l, e_list) dump_tracking_vrrp(fp, top); } void free_vscript(vrrp_script_t *vscript) { list_del_init(&vscript->e_list); free_tracking_obj_list(&vscript->tracking_vrrp); FREE_CONST(vscript->sname); notify_free_script(&vscript->script); FREE(vscript); } static void free_vscript_list(list_head_t *l) { vrrp_script_t *vscript, *vscript_tmp; list_for_each_entry_safe(vscript, vscript_tmp, l, e_list) free_vscript(vscript); } static void dump_vscript(FILE *fp, const vrrp_script_t *vscript) { const char *str; conf_write(fp, " VRRP Script = %s", vscript->sname); conf_write(fp, " Command = %s", cmd_str(&vscript->script)); if (vscript->script.path) conf_write(fp, " Path = %s", vscript->script.path); if (vscript->interval % TIMER_HZ) conf_write(fp, " Interval = %lu.%3.3lu sec", vscript->interval / TIMER_HZ, (vscript->interval % TIMER_HZ) / 1000); else conf_write(fp, " Interval = %lu sec", vscript->interval / TIMER_HZ); if (vscript->timeout % TIMER_HZ) conf_write(fp, " Timeout = %lu.%3.3lu sec", vscript->timeout / TIMER_HZ, (vscript->timeout % TIMER_HZ) / 1000); else conf_write(fp, " Timeout = %lu sec", vscript->timeout / TIMER_HZ); conf_write(fp, " Weight = %d%s", vscript->weight, vscript->weight_reverse ? " reverse" : ""); conf_write(fp, " Rise = %d", vscript->rise); conf_write(fp, " Fall = %d", vscript->fall); conf_write(fp, " Result = %d", vscript->result); conf_write(fp, " Insecure = %s", vscript->insecure ? "yes" : "no"); switch (vscript->init_state) { case SCRIPT_INIT_STATE_INIT: str = "INIT"; break; case SCRIPT_INIT_STATE_FAILED: str = "INIT/FAILED"; break; case SCRIPT_INIT_STATE_INIT_RELOAD: str = "INIT/RELOAD"; break; default: str = "unknown"; } conf_write(fp, " Init state = %s", str); conf_write(fp, " Status = %s", vscript->result >= vscript->rise ? "GOOD" : "BAD"); conf_write(fp, " Script uid:gid = %u:%u", vscript->script.uid, vscript->script.gid); conf_write(fp, " VRRP instances :"); dump_tracking_obj_list(fp, &vscript->tracking_vrrp, dump_tracking_vrrp); conf_write(fp, " State = %s", vscript->state == SCRIPT_STATE_IDLE ? "idle" : vscript->state == SCRIPT_STATE_RUNNING ? "running" : vscript->state == SCRIPT_STATE_REQUESTING_TERMINATION ? "requested termination" : vscript->state == SCRIPT_STATE_FORCING_TERMINATION ? "forcing termination" : "unknown"); } static void dump_vscript_list(FILE *fp, const list_head_t *l) { vrrp_script_t *script; list_for_each_entry(script, l, e_list) dump_vscript(fp, script); } #ifdef _WITH_TRACK_PROCESS_ void free_vprocess(vrrp_tracked_process_t *vprocess) { list_del_init(&vprocess->e_list); free_tracking_obj_list(&vprocess->tracking_vrrp); FREE_CONST(vprocess->pname); FREE_CONST(vprocess->process_path); FREE_CONST_PTR(vprocess->process_params); FREE(vprocess); } static void free_vprocess_list(list_head_t *l) { vrrp_tracked_process_t *vprocess, *vprocess_tmp; list_for_each_entry_safe(vprocess, vprocess_tmp, l, e_list) free_vprocess(vprocess); } static void dump_vprocess(FILE *fp, const vrrp_tracked_process_t *vprocess) { char *params, *p; conf_write(fp, " VRRP Track process = %s", vprocess->pname); conf_write(fp, " Process = %s", vprocess->process_path); if (vprocess->process_params) { params = MALLOC(vprocess->process_params_len); memcpy(params, vprocess->process_params, vprocess->process_params_len); p = params; for (p = strchr(params, '\0'); p < params + vprocess->process_params_len - 1; p = strchr(params + 1, '\0')) *p = ' '; conf_write(fp, " Parameters = %s", params); FREE(params); } if (vprocess->param_match != PARAM_MATCH_NONE) conf_write(fp, " Param match%s", vprocess->param_match == PARAM_MATCH_EXACT ? "" : vprocess->param_match == PARAM_MATCH_PARTIAL ? " = partial" : vprocess->param_match == PARAM_MATCH_INITIAL ? " = initial" : "unknown"); conf_write(fp, " Min processes = %u", vprocess->quorum); if (vprocess->quorum_max < UINT_MAX) conf_write(fp, " Max processes = %u", vprocess->quorum_max); conf_write(fp, " Current processes = %u", vprocess->num_cur_proc); conf_write(fp, " Have quorum = %s", vprocess->have_quorum ? "true" : "false"); conf_write(fp, " Weight = %d%s", vprocess->weight, vprocess->weight_reverse ? " reverse" : ""); conf_write(fp, " Terminate delay = %fs", (double)vprocess->terminate_delay / TIMER_HZ); conf_write(fp, " Fork delay = %fs", (double)vprocess->fork_delay / TIMER_HZ); if (fp) { conf_write(fp, " Fork delay timer %srunning", vprocess->fork_timer_thread ? "" : "not "); conf_write(fp, " Terminate delay timer %srunning", vprocess->terminate_timer_thread ? "" : "not "); } conf_write(fp, " Full command = %s", vprocess->full_command ? "true" : "false"); dump_tracking_obj_list(fp, &vprocess->tracking_vrrp, dump_tracking_vrrp); } static void dump_vprocess_list(FILE *fp, const list_head_t *l) { vrrp_tracked_process_t *vprocess; list_for_each_entry(vprocess, l, e_list) dump_vprocess(fp, vprocess); } #endif #ifdef _WITH_BFD_ void free_vrrp_tracked_bfd(vrrp_tracked_bfd_t *vbfd) { list_del_init(&vbfd->e_list); free_tracking_obj_list(&vbfd->tracking_vrrp); FREE(vbfd); } static void free_vrrp_tracked_bfd_list(list_head_t *l) { vrrp_tracked_bfd_t *vbfd, *vbfd_tmp; list_for_each_entry_safe(vbfd, vbfd_tmp, l, e_list) free_vrrp_tracked_bfd(vbfd); } static void dump_vrrp_tracked_bfd(FILE *fp, const vrrp_tracked_bfd_t *vbfd) { conf_write(fp, " VRRP Track BFD = %s", vbfd->bname); conf_write(fp, " Weight = %d%s", vbfd->weight, vbfd->weight_reverse ? " reverse" : ""); conf_write(fp, " Bfd is %s", vbfd->bfd_up ? "up" : "down"); conf_write(fp, " Tracking VRRP instances :"); dump_tracking_obj_list(fp, &vbfd->tracking_vrrp, dump_tracking_vrrp); } static void dump_vrrp_tracked_bfd_list(FILE *fp, const list_head_t *l) { vrrp_tracked_bfd_t *vbfd; list_for_each_entry(vbfd, l, e_list) dump_vrrp_tracked_bfd(fp, vbfd); } #endif /* Socket pool functions */ static void free_sock(sock_t *sock) { /* First of all cancel pending thread. If we are reloading * thread_cleanup_master() has already been called, and so * the thread already will have been cancelled. */ if (!reload) thread_cancel(sock->thread); /* Close related socket */ if (sock->fd_in > 0) close(sock->fd_in); if (sock->fd_out > 0) close(sock->fd_out); FREE(sock); } void free_sock_list(list_head_t *l) { sock_t *sock, *sock_tmp; list_for_each_entry_safe(sock, sock_tmp, l, e_list) free_sock(sock); } static void dump_sock(FILE *fp, const sock_t *sock) { conf_write(fp, "VRRP sockpool: [ifindex(%3u), family(%s), proto(%d), fd(%d,%d) %sicast, address(%s)]" , sock->ifp ? sock->ifp->ifindex : 0 , sock->family == AF_INET ? "IPv4" : sock->family == AF_INET6 ? "IPv6" : "unknown" , sock->proto , sock->fd_in , sock->fd_out , sock->unicast_src ? ", un" : "mult" , sock->unicast_src ? inet_sockaddrtos(sock->unicast_src) : inet_sockaddrtos(sock->mcast_daddr) ); } void dump_sock_list(FILE *fp, const list_head_t *l) { sock_t *sock; list_for_each_entry(sock, l, e_list) dump_sock(fp, sock); } static void dump_sock_pool(FILE *fp, const list_head_t *l) { sock_t *sock; const vrrp_t *vrrp; list_for_each_entry(sock, l, e_list) { conf_write(fp, " fd_in %d fd_out = %d", sock->fd_in, sock->fd_out); conf_write(fp, " Interface = %s", sock->ifp->ifname); #ifdef _HAVE_VRF_ if (sock->vrf_ifp) conf_write(fp, " VRF = %s", sock->vrf_ifp->ifname); #endif conf_write(fp, " Family = %s", sock->family == AF_INET ? "IPv4" : sock->family == AF_INET6 ? "IPv6" : "unknown"); conf_write(fp, " Protocol = %s", sock->proto == IPPROTO_AH ? "AH" : sock->proto == IPPROTO_VRRP ? "VRRP" : "unknown"); conf_write(fp, " Type = %sicast", sock->unicast_src ? "Un" : "Mult"); if (sock->unicast_src) // Also for mcast once can specify conf_write(fp, " Address = %s", inet_sockaddrtos(sock->unicast_src)); conf_write(fp, " Rx buf size = %d", sock->rx_buf_size); conf_write(fp, " VRRP instances"); rb_for_each_entry_const(vrrp, &sock->rb_vrid, rb_vrid) conf_write(fp, " %s vrid %d", vrrp->iname, vrrp->vrid); } } static void free_unicast_peer(unicast_peer_t *peer) { list_del_init(&peer->e_list); FREE(peer); } static void free_unicast_peer_list(list_head_t *l) { unicast_peer_t *peer, *peer_tmp; list_for_each_entry_safe(peer, peer_tmp, l, e_list) free_unicast_peer(peer); } static void dump_unicast_peer(FILE *fp, const void *data) { const unicast_peer_t *peer = data; conf_write(fp, " %s min_ttl %u max_ttl %u", inet_sockaddrtos(&peer->address), peer->min_ttl, peer->max_ttl); #ifdef _CHECKSUM_DEBUG_ conf_write(fp, " last rx checksum = 0x%4.4x, priority %d", peer->chk.last_rx_checksum, peer->chk.last_rx_priority); conf_write(fp, " last tx checksum = 0x%4.4x, priority %d", peer->chk.last_tx_checksum, peer->chk.last_tx_priority); #endif } static void dump_unicast_peer_list(FILE *fp, const list_head_t *l) { unicast_peer_t *peer; list_for_each_entry(peer, l, e_list) dump_unicast_peer(fp, peer); } static void free_vrrp(vrrp_t *vrrp) { FREE_CONST(vrrp->iname); #ifdef _HAVE_VRRP_IPVLAN_ FREE_PTR(vrrp->ipvlan_addr); #endif FREE_PTR(vrrp->send_buffer); free_notify_script(&vrrp->script_backup); free_notify_script(&vrrp->script_master); free_notify_script(&vrrp->script_fault); free_notify_script(&vrrp->script_stop); free_notify_script(&vrrp->script_deleted); free_notify_script(&vrrp->script); free_notify_script(&vrrp->script_master_rx_lower_pri); FREE_PTR(vrrp->stats); free_track_if_list(&vrrp->track_ifp); free_track_script_list(&vrrp->track_script); free_track_file_monitor_list(&vrrp->track_file); #ifdef _WITH_TRACK_PROCESS_ free_track_process_list(&vrrp->track_process); #endif #ifdef _WITH_BFD_ free_track_bfd_list(&vrrp->track_bfd); #endif free_unicast_peer_list(&vrrp->unicast_peer); free_ipaddress_list(&vrrp->vip); free_ipaddress_list(&vrrp->evip); free_iproute_list(&vrrp->vroutes); free_iprule_list(&vrrp->vrules); list_del_init(&vrrp->e_list); FREE(vrrp); } static void free_vrrp_list(list_head_t *l) { vrrp_t *vrrp, *vrrp_tmp; list_for_each_entry_safe(vrrp, vrrp_tmp, l, e_list) free_vrrp(vrrp); } static void dump_vrrp(FILE *fp, const vrrp_t *vrrp) { #ifdef _WITH_VRRP_AUTH_ char auth_data[sizeof(vrrp->auth_data) + 1]; #endif char time_str[32]; // Allow for decimal point and microseconds /* If fp is NULL, we are writing configuration to syslog at * startup, so there is no point writing transient state information. */ conf_write(fp, " VRRP Instance = %s", vrrp->iname); conf_write(fp, " VRRP Version = %d", vrrp->version); if (vrrp->sync) conf_write(fp, " Sync group = %s", vrrp->sync->gname); if (vrrp->family == AF_INET6) conf_write(fp, " Using Native IPv6"); if (fp) { conf_write(fp, " State = %s", get_state_str(vrrp->state)); if (vrrp->state == VRRP_STATE_BACK) { conf_write(fp, " Master router = %s", inet_sockaddrtos(&vrrp->master_saddr)); conf_write(fp, " Master priority = %d", vrrp->master_priority); if (vrrp->version == VRRP_VERSION_3) conf_write(fp, " Master advert interval = %u milli-sec", vrrp->master_adver_int / (TIMER_HZ / 1000)); } else if (vrrp->state == VRRP_STATE_MAST && vrrp->base_priority == VRRP_PRIO_OWNER) { conf_write(fp, " Rogue master counter = %u", vrrp->rogue_counter); conf_write(fp, " Roger timer thread = %p", vrrp->rogue_timer_thread); if (vrrp->rogue_counter || vrrp->rogue_timer_thread) conf_write(fp, " Roger adver interval = %u ms", vrrp->rogue_adver_int / (TIMER_HZ / 1000)); } } if (vrrp->flags) { conf_write(fp, " Flags:"); if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) conf_write(fp, " Using unicast%s", __test_bit(VRRP_FLAG_UNICAST_DUPLICATE_VRID, &vrrp->flags) ? " with VRID duplication" : ""); else if (__test_bit(VRRP_FLAG_UNICAST_CONFIGURED, &vrrp->flags)) conf_write(fp, " Unicast config option specified but using multicast"); if (__test_bit(VRRP_FLAG_UNICAST_FAULT_NO_PEERS, &vrrp->flags)) conf_write(fp, " If unicast go to fault state if no peers"); } else conf_write(fp, " Flags: none"); if (vrrp->rlflags) conf_write(fp, " Rate-limit flags = 0x%x", vrrp->rlflags); conf_write(fp, " Wantstate = %s", get_state_str(vrrp->wantstate)); conf_write(fp, " Number of config faults = %u", vrrp->num_config_faults); if (fp) { conf_write(fp, " Number of tracker faults = %u", vrrp->num_track_fault); if (!vrrp->flags_if_fault) conf_write(fp, " Flags of interface faults - (none)"); else { conf_write(fp, " Flags of interface faults:"); if (__test_bit(VRRP_FAULT_FL_TRACKER, &vrrp->flags_if_fault)) conf_write(fp, " unspecified"); if (__test_bit(VRRP_FAULT_FL_INTERFACE_DOWN, &vrrp->flags_if_fault)) conf_write(fp, " i/f down"); #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_FAULT_FL_BASE_INTERFACE_DOWN, &vrrp->flags_if_fault)) conf_write(fp, " base i/f down"); #endif if (__test_bit(VRRP_FAULT_FL_DUPLICATE_VRID, &vrrp->flags_if_fault)) conf_write(fp, " duplicate VRID"); if (__test_bit(VRRP_FAULT_FL_NO_SOURCE_IP, &vrrp->flags_if_fault)) conf_write(fp, " no source IP address"); if (__test_bit(VRRP_FAULT_FL_CONFIG_ERROR, &vrrp->flags_if_fault)) conf_write(fp, " config error"); } #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_FLAG_DUPLICATE_VRID_FAULT, &vrrp->flags)) conf_write(fp, " Duplicate VRID"); #endif conf_write(fp, " Number of track scripts init = %u", vrrp->num_script_init); conf_write(fp, " Last transition = %" PRI_tv_sec ".%6.6" PRI_tv_usec " (%s)", vrrp->last_transition.tv_sec, vrrp->last_transition.tv_usec, ctime_us_r(&vrrp->last_transition, time_str)); if (!ctime_r(&vrrp->sands.tv_sec, time_str)) strcpy(time_str, "invalid time "); if (vrrp->sands.tv_sec == TIMER_DISABLED) conf_write(fp, " Read timeout = DISABLED"); else conf_write(fp, " Read timeout = %" PRI_tv_sec ".%6.6" PRI_tv_usec " (%s)", vrrp->sands.tv_sec, vrrp->sands.tv_usec, ctime_us_r(&vrrp->sands, time_str)); conf_write(fp, " Master down timer = %u usecs", vrrp->ms_down_timer); } #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags)) { conf_write(fp, " Use VMAC, i/f name %s, is_up = %s, xmit_base = %s", vrrp->vmac_ifname, __test_bit(VRRP_VMAC_UP_BIT, &vrrp->flags) ? "true" : "false", __test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) ? "true" : "false"); if (__test_bit(VRRP_VMAC_MAC_SPECIFIED, &vrrp->flags)) conf_write(fp, " MAC = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x%s", vrrp->ll_addr[0], vrrp->ll_addr[1], vrrp->ll_addr[2], vrrp->ll_addr[3], vrrp->ll_addr[4], vrrp->ll_addr[5], __test_bit(VRRP_VMAC_MAC_USE_VRID, &vrrp->flags) ? " (using VRID)" : ""); } #ifdef _HAVE_VRRP_IPVLAN_ else if (__test_bit(VRRP_IPVLAN_BIT, &vrrp->flags)) conf_write(fp, " Use IPVLAN, i/f %s, is_up = %s%s%s, type %s", vrrp->vmac_ifname, __test_bit(VRRP_VMAC_UP_BIT, &vrrp->flags) ? "true" : "false", vrrp->ipvlan_addr ? ", i/f address = " : "", vrrp->ipvlan_addr ? ipaddresstos(NULL, vrrp->ipvlan_addr) : "", #if HAVE_DECL_IFLA_IPVLAN_FLAGS /* Since Linux v4.15 */ !vrrp->ipvlan_type ? "bridge" : vrrp->ipvlan_type == IPVLAN_F_PRIVATE ? "private" : vrrp->ipvlan_type == IPVLAN_F_VEPA ? "vepa" : "unknown" #else "bridge" #endif ); #endif /* The following two flags should only be set on VMACs, but * we check them for any interface type, just incase ... */ if (__test_bit(VRRP_VMAC_NETLINK_NOTIFY, &vrrp->flags)) conf_write(fp, " Force netlink update for base interface"); if (__test_bit(VRRP_VMAC_ADDR_BIT, &vrrp->flags)) conf_write(fp, " Use VMAC for VIPs on other interfaces"); /* The following should only be specified for VMACs and ipvlans */ if (__test_bit(VRRP_VMAC_GROUP, &vrrp->flags)) conf_write(fp, " Interface group %u", vrrp->vmac_group); else if (vrrp->ifp && vrrp->ifp->base_ifp->group) conf_write(fp, " Interface group %u (copied from parent)", vrrp->ifp->base_ifp->group); if (vrrp->ifp && vrrp->ifp->is_ours) { conf_write(fp, " Interface = %s, %s on %s%s", IF_NAME(vrrp->ifp), __test_bit(VRRP_VMAC_BIT, &vrrp->flags) ? "vmac" : "ipvlan", vrrp->ifp != vrrp->ifp->base_ifp ? vrrp->ifp->base_ifp->ifname : "(unknown)", __test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) ? ", xmit base i/f" : ""); } else #endif conf_write(fp, " Interface = %s", vrrp->ifp ? IF_NAME(vrrp->ifp) : "not configured"); #ifdef _HAVE_VRRP_VMAC_ if (vrrp->ifp && vrrp->configured_ifp && vrrp->configured_ifp != vrrp->ifp->base_ifp && vrrp->ifp->is_ours) conf_write(fp, " Configured interface = %s", vrrp->configured_ifp->ifname); #endif #ifdef _HAVE_VRF_ if (vrrp->vrf_ifp) conf_write(fp, " VRF = %s", vrrp->vrf_ifp->ifname); #endif if (__test_bit(VRRP_FLAG_DONT_TRACK_PRIMARY, &vrrp->flags)) conf_write(fp, " VRRP interface tracking disabled"); if (__test_bit(VRRP_FLAG_SKIP_CHECK_ADV_ADDR, &vrrp->flags)) conf_write(fp, " Skip checking advert IP addresses"); if (__test_bit(VRRP_FLAG_ALLOW_NO_VIPS, &vrrp->flags)) conf_write(fp, " Suppress no VIPs warning"); if (vrrp->strict_mode) conf_write(fp, " Enforcing strict VRRP compliance"); conf_write(fp, " Using src_ip = %s%s", vrrp->saddr.ss_family != AF_UNSPEC ? inet_sockaddrtos(&vrrp->saddr) : "(none)", __test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags) ? " (from configuration)" : ""); if (!__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { if (vrrp->family == AF_INET) conf_write(fp, " Multicast address %s", inet_sockaddrtos(&vrrp->mcast_daddr)); else conf_write(fp, " Multicast address %s, ifindex %u", inet_sockaddrtos(&vrrp->mcast_daddr), PTR_CAST_CONST(struct sockaddr_in6, &vrrp->mcast_daddr)->sin6_scope_id); } conf_write(fp, " Gratuitous ARP delay = %u", vrrp->garp_delay/TIMER_HZ); conf_write(fp, " Gratuitous ARP repeat = %u", vrrp->garp_rep); conf_write(fp, " Gratuitous ARP refresh = %" PRI_tv_sec, vrrp->garp_refresh.tv_sec); conf_write(fp, " Gratuitous ARP refresh repeat = %u", vrrp->garp_refresh_rep); conf_write(fp, " Gratuitous ARP lower priority delay = %u", vrrp->garp_lower_prio_delay / TIMER_HZ); conf_write(fp, " Gratuitous ARP lower priority repeat = %u", vrrp->garp_lower_prio_rep); conf_write(fp, " Down timer adverts = %u", vrrp->down_timer_adverts); #ifdef _HAVE_VRRP_VMAC_ if (vrrp->vmac_garp_intvl.tv_sec) { conf_write(fp, " Gratuitous ARP for each secondary %s = %" PRI_time_t, __test_bit(VRRP_FLAG_VMAC_GARP_ALL_IF, &vrrp->flags) ? "i/f" : "VMAC", vrrp->vmac_garp_intvl.tv_sec); } #endif conf_write(fp, " Send advert after receive lower priority advert = %s", vrrp->lower_prio_no_advert ? "false" : "true"); conf_write(fp, " Send advert after receive higher priority advert = %s", vrrp->higher_prio_send_advert ? "true" : "false"); if (vrrp->base_priority == VRRP_PRIO_OWNER) conf_write(fp, " Address owner ignores received adverts = %s", vrrp->owner_ignore_adverts ? "true" : "false"); conf_write(fp, " Virtual Router ID = %d", vrrp->vrid); conf_write(fp, " Priority = %d", vrrp->base_priority); if (fp) { conf_write(fp, " Effective priority = %d", vrrp->effective_priority); conf_write(fp, " Total priority = %d", vrrp->total_priority); } if (__test_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags)) conf_write(fp, " Highest other priority = %u", vrrp->highest_other_priority); conf_write(fp, " Advert interval = %u %s", (vrrp->version == VRRP_VERSION_2) ? (vrrp->adver_int / TIMER_HZ) : (vrrp->adver_int / (TIMER_HZ / 1000)), (vrrp->version == VRRP_VERSION_2) ? "sec" : "milli-sec"); conf_write(fp, " Last advert sent = %" PRI_tv_sec ".%6.6" PRI_tv_usec, vrrp->last_advert_sent.tv_sec, vrrp->last_advert_sent.tv_usec); #ifdef _WITH_FIREWALL_ conf_write(fp, " Accept = %s", vrrp->accept ? "enabled" : "disabled"); #endif conf_write(fp, " Preempt = %s", __test_bit(VRRP_FLAG_NOPREEMPT, &vrrp->flags) ? "disabled" : "enabled"); if (vrrp->preempt_delay) conf_write(fp, " Preempt delay = %g secs", vrrp->preempt_delay / TIMER_HZ_DOUBLE); conf_write(fp, " Promote_secondaries = %s", __test_bit(VRRP_FLAG_PROMOTE_SECONDARIES, &vrrp->flags) ? "enabled" : "disabled"); #if defined _WITH_VRRP_AUTH_ if (vrrp->auth_type) { conf_write(fp, " Authentication type = %s", (vrrp->auth_type == VRRP_AUTH_AH) ? "IPSEC_AH" : "SIMPLE_PASSWORD"); if (vrrp->auth_type != VRRP_AUTH_AH) { /* vrrp->auth_data is not \0 terminated */ memcpy(auth_data, vrrp->auth_data, sizeof(vrrp->auth_data)); auth_data[sizeof(vrrp->auth_data)] = '\0'; conf_write(fp, " Password = %s", auth_data); } } else if (vrrp->version == VRRP_VERSION_2) conf_write(fp, " Authentication type = none"); #endif if (vrrp->version == VRRP_VERSION_3 && vrrp->family == AF_INET) conf_write(fp, " VRRPv3 uses VRRPv2 checksum = %s", __test_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, &vrrp->flags) ? "enabled" : "disabled"); if (vrrp->kernel_rx_buf_size) conf_write(fp, " Kernel rx buffer size = %zu", vrrp->kernel_rx_buf_size); if (vrrp->debug) conf_write(fp, " Debug level = %d", vrrp->debug); #ifdef _CHECKSUM_DEBUG_ conf_write(fp, " last rx checksum = 0x%4.4x, priority %d", vrrp->chk.last_rx_checksum, vrrp->chk.last_rx_priority); conf_write(fp, " last tx checksum = 0x%4.4x, priority %d", vrrp->chk.last_tx_checksum, vrrp->chk.last_tx_priority); #endif if (!list_empty(&vrrp->vip)) { conf_write(fp, " Virtual IP (%u):", vrrp->vip_cnt); dump_ipaddress_list(fp, &vrrp->vip); } if (!list_empty(&vrrp->evip)) { conf_write(fp, " Virtual IP Excluded :"); dump_ipaddress_list(fp, &vrrp->evip); } if (__test_bit(VRRP_FLAG_UNICAST, &vrrp->flags)) { if (vrrp->ttl != -1) conf_write(fp, " Unicast TTL = %d", vrrp->ttl); conf_write(fp, " Check unicast src : %s", __test_bit(VRRP_FLAG_CHECK_UNICAST_SRC, &vrrp->flags) ? "yes" : "no"); conf_write(fp, " Unicast Peer :"); dump_unicast_peer_list(fp, &vrrp->unicast_peer); #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ conf_write(fp, " Unicast checksum compatibility = %s", vrrp->unicast_chksum_compat == CHKSUM_COMPATIBILITY_NONE ? "no" : vrrp->unicast_chksum_compat == CHKSUM_COMPATIBILITY_NEVER ? "never" : vrrp->unicast_chksum_compat == CHKSUM_COMPATIBILITY_CONFIG ? "config" : vrrp->unicast_chksum_compat == CHKSUM_COMPATIBILITY_AUTO ? "auto" : "unknown"); #endif } if (vrrp->sockets) conf_write(fp, " fd_in %d, fd_out %d", vrrp->sockets->fd_in, vrrp->sockets->fd_out); else conf_write(fp, " No sockets allocated"); if (!list_empty(&vrrp->vroutes)) { conf_write(fp, " Virtual Routes :"); dump_iproute_list(fp, &vrrp->vroutes); } if (!list_empty(&vrrp->vrules)) { conf_write(fp, " Virtual Rules :"); dump_iprule_list(fp, &vrrp->vrules); } if (!list_empty(&vrrp->track_ifp)) { conf_write(fp, " Tracked interfaces :"); dump_track_if_list(fp, &vrrp->track_ifp); } if (!list_empty(&vrrp->track_script)) { conf_write(fp, " Tracked scripts :"); dump_track_script_list(fp, &vrrp->track_script); } if (!list_empty(&vrrp->track_file)) { conf_write(fp, " Tracked files :"); dump_track_file_monitor_list(fp, &vrrp->track_file); } #ifdef _WITH_TRACK_PROCESS_ if (!list_empty(&vrrp->track_process)) { conf_write(fp, " Tracked processes :"); dump_track_process_list(fp, &vrrp->track_process); } #endif #ifdef _WITH_BFD_ if (!list_empty(&vrrp->track_bfd)) { conf_write(fp, " Tracked BFDs :"); dump_tracked_bfd_list(fp, &vrrp->track_bfd); } #endif conf_write(fp, " Using smtp notification = %s", vrrp->smtp_alert ? "yes" : "no"); conf_write(fp, " Notify deleted = %s", vrrp->notify_deleted ? "Deleted" : "Fault"); if (vrrp->script_backup) dump_notify_script(fp, vrrp->script_backup, "Backup"); if (vrrp->script_master) dump_notify_script(fp, vrrp->script_master, "Master"); if (vrrp->script_fault) dump_notify_script(fp, vrrp->script_fault, "Fault"); if (vrrp->script_stop) dump_notify_script(fp, vrrp->script_stop, "Stop"); if (vrrp->script_deleted) dump_notify_script(fp, vrrp->script_deleted, "Deleted"); if (vrrp->script) dump_notify_script(fp, vrrp->script, "Generic"); if (vrrp->script_master_rx_lower_pri) dump_notify_script(fp, vrrp->script_master_rx_lower_pri, "Master rx lower pri"); conf_write(fp, " Notify priority changes = %s", vrrp->notify_priority_changes ? "true" : "false"); } static void dump_vrrp_list(FILE *fp, const list_head_t *l) { vrrp_t *vrrp; list_for_each_entry(vrrp, l, e_list) dump_vrrp(fp, vrrp); } vrrp_sgroup_t * alloc_vrrp_sync_group(const char *gname) { vrrp_sgroup_t *new; /* Allocate new VRRP group structure */ PMALLOC(new); INIT_LIST_HEAD(&new->e_list); INIT_LIST_HEAD(&new->vrrp_instances); INIT_LIST_HEAD(&new->track_ifp); INIT_LIST_HEAD(&new->track_script); INIT_LIST_HEAD(&new->track_file); #ifdef _WITH_TRACK_PROCESS_ INIT_LIST_HEAD(&new->track_process); #endif #ifdef _WITH_BFD_ INIT_LIST_HEAD(&new->track_bfd); #endif new->state = VRRP_STATE_INIT; new->last_email_state = VRRP_STATE_INIT; new->gname = STRDUP(gname); new->sgroup_tracking_weight = false; new->smtp_alert = -1; new->notify_priority_changes = -1; return new; } static vrrp_stats * alloc_vrrp_stats(void) { vrrp_stats *new; PMALLOC(new); new->become_master = 0; new->release_master = 0; new->invalid_authtype = 0; #ifdef _WITH_VRRP_AUTH_ new->authtype_mismatch = 0; new->auth_failure = 0; #endif new->packet_len_err = 0; new->advert_rcvd = 0; new->advert_sent = 0; new->advert_interval_err = 0; new->ip_ttl_err = 0; new->pri_zero_rcvd = 0; new->pri_zero_sent = 0; new->invalid_type_rcvd = 0; new->addr_list_err = 0; #ifdef _WITH_SNMP_RFCV3_ new->master_reason = VRRPV3_MASTER_REASON_NOT_MASTER; new->next_master_reason = VRRPV3_MASTER_REASON_MASTER_NO_RESPONSE; #endif return new; } vrrp_t * alloc_vrrp(const char *iname) { vrrp_t *new; /* Allocate new VRRP structure */ PMALLOC(new); INIT_LIST_HEAD(&new->e_list); INIT_LIST_HEAD(&new->s_list); INIT_LIST_HEAD(&new->track_ifp); INIT_LIST_HEAD(&new->track_script); INIT_LIST_HEAD(&new->track_file); INIT_LIST_HEAD(&new->unicast_peer); #ifdef _WITH_TRACK_PROCESS_ INIT_LIST_HEAD(&new->track_process); #endif #ifdef _WITH_BFD_ INIT_LIST_HEAD(&new->track_bfd); #endif INIT_LIST_HEAD(&new->vip); INIT_LIST_HEAD(&new->evip); INIT_LIST_HEAD(&new->vroutes); INIT_LIST_HEAD(&new->vrules); /* Set default values */ new->family = AF_UNSPEC; new->saddr.ss_family = AF_UNSPEC; new->wantstate = VRRP_STATE_INIT; new->last_email_state = VRRP_STATE_INIT; new->version = 0; new->master_priority = 0; new->last_transition = timer_now(); new->iname = STRDUP(iname); new->stats = alloc_vrrp_stats(); new->ttl = -1; #ifdef _WITH_FIREWALL_ new->accept = PARAMETER_UNSET; #endif new->garp_rep = global_data->vrrp_garp_rep; new->garp_refresh = global_data->vrrp_garp_refresh; new->garp_refresh_rep = global_data->vrrp_garp_refresh_rep; new->garp_delay = global_data->vrrp_garp_delay; new->garp_lower_prio_delay = PARAMETER_UNSET; new->garp_lower_prio_rep = PARAMETER_UNSET; new->down_timer_adverts = global_data->vrrp_down_timer_adverts; #ifdef _HAVE_VRRP_VMAC_ new->vmac_garp_intvl.tv_sec = TIME_T_PARAMETER_UNSET; #endif new->lower_prio_no_advert = PARAMETER_UNSET; new->higher_prio_send_advert = PARAMETER_UNSET; #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ new->unicast_chksum_compat = CHKSUM_COMPATIBILITY_NONE; #endif new->owner_ignore_adverts = PARAMETER_UNSET; if (global_data->v3_checksum_as_v2) __set_bit(VRRP_FLAG_V3_CHECKSUM_AS_V2, &new->flags); new->smtp_alert = -1; new->notify_priority_changes = -1; if (global_data->vrrp_skip_check_adv_addr) __set_bit(VRRP_FLAG_SKIP_CHECK_ADV_ADDR, &new->flags); new->strict_mode = PARAMETER_UNSET; return new; } void alloc_vrrp_unicast_peer(const vector_t *strvec) { unicast_peer_t *peer; unsigned ttl; unsigned i; unicast_peer_t *existing_peer; /* Allocate new unicast peer */ PMALLOC(peer); peer->min_ttl = 0; peer->max_ttl = 255; if (inet_stosockaddr(strvec_slot(strvec, 0), NULL, &peer->address)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: VRRP instance[%s] malformed unicast" " peer address[%s]. Skipping..." , current_vrrp->iname, strvec_slot(strvec, 0)); FREE(peer); return; } if (!current_vrrp->family) current_vrrp->family = peer->address.ss_family; else if (peer->address.ss_family != current_vrrp->family) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: VRRP instance[%s] and unicast peer address" "[%s] MUST be of the same family !!! Skipping..." , current_vrrp->iname, strvec_slot(strvec, 0)); FREE(peer); return; } for (i = 1; i < vector_size(strvec); i += 2) { if (i + 1 >= vector_size(strvec)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) %s - %s is missing a value", current_vrrp->iname, strvec_slot(strvec, 0), strvec_slot(strvec, i)); break; } if (read_unsigned(strvec_slot(strvec, i + 1), &ttl, 0, 255, false)) { if (!strcmp(strvec_slot(strvec, i), "min_ttl")) peer->min_ttl = ttl; else if (!strcmp(strvec_slot(strvec, i), "max_ttl")) peer->max_ttl = ttl; else { report_config_error(CONFIG_GENERAL_ERROR, "(%s) %s - unknown unicast_peer option %s", current_vrrp->iname, strvec_slot(strvec, 0), strvec_slot(strvec, i)); break; } __set_bit(VRRP_FLAG_CHECK_UNICAST_SRC, ¤t_vrrp->flags); } } /* Check this unicast peer is not already configured */ list_for_each_entry(existing_peer, ¤t_vrrp->unicast_peer, e_list) { if ((current_vrrp->family == AF_INET && ((struct sockaddr_in *)&peer->address)->sin_addr.s_addr == ((struct sockaddr_in *)&existing_peer->address)->sin_addr.s_addr) || (current_vrrp->family == AF_INET6 && !memcmp(&((struct sockaddr_in6 *)&peer->address)->sin6_addr, &((struct sockaddr_in6 *)&existing_peer->address)->sin6_addr, sizeof(((struct sockaddr_in6 *)&peer->address)->sin6_addr)))) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) %s - duplicate unicast_peer", current_vrrp->iname, strvec_slot(strvec, 0)); FREE(peer); return; } } if (peer->min_ttl > peer->max_ttl) report_config_error(CONFIG_GENERAL_ERROR, "(%s) %s - min_ttl %u > max_ttl %u - all packets will be discarded", current_vrrp->iname, strvec_slot(strvec, 0), peer->min_ttl, peer->max_ttl); list_add_tail(&peer->e_list, ¤t_vrrp->unicast_peer); } void alloc_vrrp_track_if(const vector_t *strvec) { alloc_track_if(current_vrrp->iname, ¤t_vrrp->track_ifp, strvec); } void alloc_vrrp_track_script(const vector_t *strvec) { alloc_track_script(current_vrrp->iname, ¤t_vrrp->track_script, strvec); } void alloc_vrrp_track_file(const vector_t *strvec) { vrrp_alloc_track_file(current_vrrp->iname, &vrrp_data->vrrp_track_files, ¤t_vrrp->track_file, strvec); } #ifdef _WITH_TRACK_PROCESS_ void alloc_vrrp_track_process(const vector_t *strvec) { alloc_track_process(current_vrrp->iname, ¤t_vrrp->track_process, strvec); } #endif #ifdef _WITH_BFD_ void alloc_vrrp_track_bfd(const vector_t *strvec) { alloc_track_bfd(current_vrrp->iname, ¤t_vrrp->track_bfd, strvec); } #endif void alloc_vrrp_group_track_if(const vector_t *strvec) { alloc_track_if(current_vsyncg->gname, ¤t_vsyncg->track_ifp, strvec); } void alloc_vrrp_group_track_script(const vector_t *strvec) { alloc_track_script(current_vsyncg->gname, ¤t_vsyncg->track_script, strvec); } void alloc_vrrp_group_track_file(const vector_t *strvec) { vrrp_alloc_track_file(current_vsyncg->gname, &vrrp_data->vrrp_track_files, ¤t_vsyncg->track_file, strvec); } #ifdef _WITH_TRACK_PROCESS_ void alloc_vrrp_group_track_process(const vector_t *strvec) { alloc_track_process(current_vsyncg->gname, ¤t_vsyncg->track_process, strvec); } #endif #ifdef _WITH_BFD_ void alloc_vrrp_group_track_bfd(const vector_t *strvec) { alloc_track_bfd(current_vsyncg->gname, ¤t_vsyncg->track_bfd, strvec); } #endif static bool vip_is_duplicate(const ip_address_t *new_ipaddr, const char *vip_str, bool excluded_vip) { ip_address_t *vip; list_for_each_entry(vip, ¤t_vrrp->vip, e_list) { /* We can have a VIP and an eVIP the same if they are on * different interfaces. */ if (excluded_vip && new_ipaddr->ifp != vip->ifp) continue; if ((IP_FAMILY(new_ipaddr) == AF_INET && new_ipaddr->u.sin.sin_addr.s_addr == vip->u.sin.sin_addr.s_addr) || (IP_FAMILY(new_ipaddr) == AF_INET6 && !memcmp(&new_ipaddr->u.sin6_addr, &vip->u.sin6_addr, sizeof(new_ipaddr->u.sin6_addr)))) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): %sVIP duplicates a VIP %s - ignoring", current_vrrp->iname, excluded_vip ? "Excluded " : "", vip_str); return true; } } list_for_each_entry(vip, ¤t_vrrp->evip, e_list) { if (IP_FAMILY(new_ipaddr) != IP_FAMILY(vip)) continue; if (new_ipaddr->ifp != vip->ifp) continue; if ((IP_FAMILY(new_ipaddr) == AF_INET && new_ipaddr->u.sin.sin_addr.s_addr == vip->u.sin.sin_addr.s_addr) || (IP_FAMILY(new_ipaddr) == AF_INET6 && !memcmp(&new_ipaddr->u.sin6_addr, &vip->u.sin6_addr, sizeof(new_ipaddr->u.sin6_addr)))) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): %sVIP duplicates an excluded VIP %s - ignoring", current_vrrp->iname, excluded_vip ? "Excluded " : "", vip_str); return true; } } return false; } void alloc_vrrp_vip(const vector_t *strvec) { ip_address_t *new_ipaddr; sa_family_t address_family; if (!(new_ipaddr = alloc_ipaddress(strvec, false))) return; address_family = IP_FAMILY(new_ipaddr); if (current_vrrp->family == AF_UNSPEC) current_vrrp->family = address_family; else if (address_family != current_vrrp->family) { report_config_error(CONFIG_GENERAL_ERROR, "(%s): address family must match VRRP instance [%s] - ignoring", current_vrrp->iname, strvec_slot(strvec, 0)); free_ipaddress(new_ipaddr); return; } /* Check we don't already have this address */ if (vip_is_duplicate(new_ipaddr, strvec_slot(strvec, 0), false)) { free_ipaddress(new_ipaddr); return; } list_add_tail(&new_ipaddr->e_list, ¤t_vrrp->vip); current_vrrp->vip_cnt++; } void alloc_vrrp_evip(const vector_t *strvec) { ip_address_t *new_ipaddr; if (!(new_ipaddr = alloc_ipaddress(strvec, false))) return; /* Check we don't already have this address */ if (vip_is_duplicate(new_ipaddr, strvec_slot(strvec, 0), true)) { free_ipaddress(new_ipaddr); return; } list_add_tail(&new_ipaddr->e_list, ¤t_vrrp->evip); } void alloc_vrrp_vroute(const vector_t *strvec) { alloc_route(¤t_vrrp->vroutes, strvec, false); } void alloc_vrrp_vrule(const vector_t *strvec) { alloc_rule(¤t_vrrp->vrules, strvec, false); } vrrp_script_t * alloc_vrrp_script(const char *sname) { vrrp_script_t *new; /* Allocate new VRRP script structure */ PMALLOC(new); INIT_LIST_HEAD(&new->e_list); INIT_LIST_HEAD(&new->tracking_vrrp); new->sname = STRDUP(sname); new->interval = VRRP_SCRIPT_DI * TIMER_HZ; new->timeout = VRRP_SCRIPT_DT * TIMER_HZ; new->weight = VRRP_SCRIPT_DW; // new->last_status = VRRP_SCRIPT_STATUS_NOT_SET; new->init_state = reload ? SCRIPT_INIT_STATE_INIT_RELOAD : SCRIPT_INIT_STATE_INIT; new->state = SCRIPT_STATE_IDLE; new->rise = 1; new->fall = 1; return new; } #ifdef _WITH_TRACK_PROCESS_ vrrp_tracked_process_t * alloc_vrrp_process(const char *pname) { vrrp_tracked_process_t *new; /* Allocate new VRRP file structure */ PMALLOC(new); INIT_LIST_HEAD(&new->e_list); new->pname = STRDUP(pname); new->quorum = 1; new->quorum_max = UINT_MAX; INIT_LIST_HEAD(&new->tracking_vrrp); return new; } #endif /* data facility functions */ void alloc_vrrp_buffer(size_t len) { if (len <= vrrp_buffer_len) return; if (vrrp_buffer) FREE(vrrp_buffer); vrrp_buffer = MALLOC(len); vrrp_buffer_len = (vrrp_buffer) ? len : 0; } void free_vrrp_buffer(void) { /* If the configuration failed, we may not have * allocated a buffer */ if (!vrrp_buffer) return; FREE(vrrp_buffer); vrrp_buffer = NULL; vrrp_buffer_len = 0; } vrrp_data_t * alloc_vrrp_data(void) { vrrp_data_t *new; PMALLOC(new); INIT_LIST_HEAD(&new->static_track_groups); INIT_LIST_HEAD(&new->static_addresses); INIT_LIST_HEAD(&new->static_routes); INIT_LIST_HEAD(&new->static_rules); INIT_LIST_HEAD(&new->vrrp_sync_group); INIT_LIST_HEAD(&new->vrrp); INIT_LIST_HEAD(&new->vrrp_script); INIT_LIST_HEAD(&new->vrrp_track_files); #ifdef _WITH_TRACK_PROCESS_ INIT_LIST_HEAD(&new->vrrp_track_processes); #endif #ifdef _WITH_BFD_ INIT_LIST_HEAD(&new->vrrp_track_bfds); #endif INIT_LIST_HEAD(&new->vrrp_socket_pool); return new; } void free_vrrp_data(vrrp_data_t ** datap) { vrrp_data_t *data = *datap; free_ipaddress_list(&data->static_addresses); free_iproute_list(&data->static_routes); free_iprule_list(&data->static_rules); free_static_track_groups_list(&data->static_track_groups); free_sync_group_list(&data->vrrp_sync_group); free_vscript_list(&data->vrrp_script); free_track_file_list(&data->vrrp_track_files); #ifdef _WITH_TRACK_PROCESS_ free_vprocess_list(&data->vrrp_track_processes); #endif #ifdef _WITH_BFD_ free_vrrp_tracked_bfd_list(&data->vrrp_track_bfds); #endif free_vrrp_list(&data->vrrp); FREE(data); *datap = NULL; } static void dump_vrrp_data(FILE *fp, const vrrp_data_t * data) { if (!list_empty(&data->static_addresses)) { conf_write(fp, "------< Static Addresses >------"); dump_ipaddress_list(fp, &data->static_addresses); } if (!list_empty(&data->static_routes)) { conf_write(fp, "------< Static Routes >------"); dump_iproute_list(fp, &data->static_routes); } if (!list_empty(&data->static_rules)) { conf_write(fp, "------< Static Rules >------"); dump_iprule_list(fp, &data->static_rules); } if (!list_empty(&data->static_track_groups)) { conf_write(fp, "------< Static Track groups >------"); dump_static_track_groups_list(fp, &data->static_track_groups); } if (!list_empty(&data->vrrp)) { conf_write(fp, "------< VRRP Topology >------"); dump_vrrp_list(fp, &data->vrrp); } if (!list_empty(&data->vrrp_socket_pool)) { conf_write(fp, "------< VRRP Sockpool >------"); dump_sock_pool(fp, &data->vrrp_socket_pool); } if (!list_empty(&data->vrrp_sync_group)) { conf_write(fp, "------< VRRP Sync groups >------"); dump_sync_group_list(fp, &data->vrrp_sync_group); } if (!list_empty(&data->vrrp_script)) { conf_write(fp, "------< VRRP Scripts >------"); dump_vscript_list(fp, &data->vrrp_script); } if (!list_empty(&data->vrrp_track_files)) { conf_write(fp, "------< VRRP Track files >------"); dump_track_file_list(fp, &data->vrrp_track_files); } #ifdef _WITH_TRACK_PROCESS_ if (!list_empty(&data->vrrp_track_processes)) { conf_write(fp, "------< VRRP Track processes >------"); dump_vprocess_list(fp, &data->vrrp_track_processes); } #endif #ifdef _WITH_BFD_ if (!list_empty(&data->vrrp_track_bfds)) { conf_write(fp, "------< VRRP Track BFDs >------"); dump_vrrp_tracked_bfd_list(fp, &data->vrrp_track_bfds); } #endif } void dump_data_vrrp(FILE *fp) { list_head_t *ifq; dump_global_data(fp, global_data); if (!list_empty(&garp_delay)) { conf_write(fp, "------< Gratuitous ARP delays >------"); dump_garp_delay_list(fp, &garp_delay); } dump_vrrp_data(fp, vrrp_data); ifq = get_interface_queue(); if (!list_empty(ifq)) { conf_write(fp, "------< Interfaces >------"); dump_interface_queue(fp, ifq); } } keepalived-2.3.3/keepalived/vrrp/vrrp_iptables.c0000664000175000017500000005020514650126247015446 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: iptables management * * Author: Alexandre Cassen, * * 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-2018 Alexandre Cassen, */ #include "config.h" #include #include #include #include #ifdef _HAVE_LIBIPTC_LINUX_NET_IF_H_COLLISION_ /* Linux 4.5 introduced a namespace collision when including * libiptc/libiptc.h due to both net/if.h and linux/if.h * being included. * * See: http://bugzilla.netfilter.org/show_bug.cgi?id=1067 * * Including net/if.h first resolves the issue. */ #include #endif #include #include #ifdef _HAVE_LIBIPSET_ #include #endif #include #include #include #include "vrrp_iptables.h" #include "global_data.h" #include "vrrp_ipaddress.h" #include "vrrp.h" #include "vrrp_firewall.h" #include "vrrp_iptables_calls.h" #ifdef _HAVE_LIBIPSET_ #include "vrrp_ipset.h" #endif #include "logger.h" #include "memory.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_firewall.h" #endif #define IPTABLES_MAX_TRIES 3 /* How many times to try adding/deleting when get EAGAIN */ typedef enum { NOT_INIT, INIT_SUCCESS, INIT_FAILED, } init_state_t; struct ipt_handle { struct iptc_handle *h4; struct ip6tc_handle *h6; bool updated_v4; bool updated_v6; #ifdef _HAVE_LIBIPSET_ struct ipset_session* session; #endif int cmd; } ; /* Element [0] is IPv4, element [1] is IPv6 */ static init_state_t setup[2]; static init_state_t vips_setup[2]; #ifdef _HAVE_VRRP_VMAC_ static init_state_t igmp_setup[2]; #endif /* The way iptables appears to work is that when we do an iptc_init, we get a * snapshot of the iptables table, which internally includes an update number. * When iptc_commit is called, it checks the update number, and if it has been * updated by someone else, returns EAGAIN. * * Note: iptc_commit only needs to be called if we are changing something. In * all cases though, iptc_free must be called. * * Rules are numbered from 0 - despite what some documentation says * * Note: as insertions/deletions are made, rule numbers are changing. * * See http://www.tldp.org/HOWTO/Querying-libiptc-HOWTO/qfunction.html for * some documentation */ static int iptables_entry(struct ipt_handle* h, uint8_t family, const char* chain_name, unsigned int rulenum, const char* target_name, const ip_address_t* src_ip_address, const ip_address_t* dst_ip_address, const char* in_iface, const char* out_iface, uint16_t protocol, uint8_t type, int cmd, uint8_t flags, bool force) { int res; if (family == AF_INET) { if (!h->h4) h->h4 = ip4tables_open ("filter"); res = ip4tables_process_entry(h->h4, chain_name, rulenum, target_name, src_ip_address, dst_ip_address, in_iface, out_iface, protocol, type, cmd, flags, force); if (!res) h->updated_v4 = true ; return res; } else { if (!h->h6) h->h6 = ip6tables_open ("filter"); res = ip6tables_process_entry(h->h6, chain_name, rulenum, target_name, src_ip_address, dst_ip_address, in_iface, out_iface, protocol, type, cmd, flags, force); if (!res) h->updated_v6 = true; return res; } return 0; } #ifdef _HAVE_LIBIPSET_ static void add_del_vip_sets(struct ipt_handle *h, int cmd, uint8_t family) { if (!global_data->using_ipsets) return; if (cmd == IPADDRESS_ADD) add_vip_ipsets(&h->session, family, false); else remove_vip_ipsets(&h->session, family); } #ifdef _HAVE_VRRP_VMAC_ static void add_del_igmp_sets(struct ipt_handle *h, int cmd, uint8_t family) { if (cmd == IPADDRESS_ADD) add_igmp_ipsets(&h->session, family, false); else remove_igmp_ipsets(&h->session, family); } #endif static void add_del_vip_rules(struct ipt_handle *h, int cmd, uint8_t family) { if (family == AF_INET) { if (h->h4 || (h->h4 = ip4tables_open("filter"))) { if (global_data->vrrp_iptables_inchain) ip4tables_add_rules(h->h4, global_data->vrrp_iptables_inchain, APPEND_RULE, IPSET_DIM_ONE, 0, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_address, IPPROTO_NONE, 0, cmd, false); if (global_data->vrrp_iptables_outchain) ip4tables_add_rules(h->h4, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_ONE, IPSET_DIM_ONE_SRC, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_address, IPPROTO_NONE, 0, cmd, false); } h->updated_v4 = true; return; } if (h->h6 || (h->h6 = ip6tables_open("filter"))) { if (global_data->vrrp_iptables_inchain) { ip6tables_add_rules(h->h6, global_data->vrrp_iptables_inchain, APPEND_RULE, IPSET_DIM_TWO, IPSET_DIM_TWO_SRC, XTC_LABEL_ACCEPT, NULL, NULL, global_data->vrrp_ipset_address_iface6, IPPROTO_ICMPV6, ND_NEIGHBOR_SOLICIT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_inchain, APPEND_RULE, IPSET_DIM_TWO, IPSET_DIM_TWO_SRC, XTC_LABEL_ACCEPT, NULL, NULL, global_data->vrrp_ipset_address_iface6, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_inchain, APPEND_RULE, IPSET_DIM_TWO, IPSET_DIM_TWO_SRC, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_address_iface6, IPPROTO_NONE, 0, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_inchain, APPEND_RULE, IPSET_DIM_ONE, 0, XTC_LABEL_ACCEPT, NULL, NULL, global_data->vrrp_ipset_address6, IPPROTO_ICMPV6, ND_NEIGHBOR_SOLICIT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_inchain, APPEND_RULE, IPSET_DIM_ONE, 0, XTC_LABEL_ACCEPT, NULL, NULL, global_data->vrrp_ipset_address6, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_inchain, APPEND_RULE, IPSET_DIM_ONE, 0, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_address6, IPPROTO_NONE, 0, cmd, false); h->updated_v6 = true; } if (global_data->vrrp_iptables_outchain) { ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, IPSET_DIM_ONE_SRC, XTC_LABEL_ACCEPT, NULL, NULL, global_data->vrrp_ipset_address_iface6, IPPROTO_ICMPV6, ND_NEIGHBOR_SOLICIT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, IPSET_DIM_ONE_SRC, XTC_LABEL_ACCEPT, NULL, NULL, global_data->vrrp_ipset_address_iface6, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, IPSET_DIM_ONE_SRC, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_address_iface6, IPPROTO_NONE, 0, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_ONE, IPSET_DIM_ONE_SRC, XTC_LABEL_ACCEPT, NULL, NULL, global_data->vrrp_ipset_address6, IPPROTO_ICMPV6, ND_NEIGHBOR_SOLICIT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_ONE, IPSET_DIM_ONE_SRC, XTC_LABEL_ACCEPT, NULL, NULL, global_data->vrrp_ipset_address6, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_ONE, IPSET_DIM_ONE_SRC, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_address6, IPPROTO_NONE, 0, cmd, false); h->updated_v6 = true; } } } #ifdef _HAVE_VRRP_VMAC_ static void add_del_igmp_rules(struct ipt_handle *h, int cmd, uint8_t family) { if (!global_data->vrrp_iptables_outchain || !global_data->using_ipsets) return; if (family == AF_INET) { if (h->h4 || (h->h4 = ip4tables_open("filter"))) { ip4tables_add_rules(h->h4, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, 0, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_igmp, IPPROTO_IGMP, 0, cmd, false); h->updated_v4 = true; } return; } if (h->h6 || (h->h6 = ip6tables_open("filter"))) { ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, 0, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_mld, IPPROTO_ICMPV6, ICMPV6_MLD2_REPORT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, 0, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_mld, IPPROTO_ICMPV6, MLD_LISTENER_REPORT, cmd, false); ip6tables_add_rules(h->h6, global_data->vrrp_iptables_outchain, APPEND_RULE, IPSET_DIM_TWO, IPSET_DIM_ONE_SRC, XTC_LABEL_DROP, NULL, NULL, global_data->vrrp_ipset_vmac_nd, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, false); h->updated_v6 = true; } } #endif #endif static struct ipt_handle* iptables_open(int cmd) { struct ipt_handle *h = MALLOC(sizeof(struct ipt_handle)); h->cmd = cmd; return h; } static int iptables_close(struct ipt_handle* h) { int res = 0; /* We need to do the sets first in case we are adding sets, and then rules * that reference them. * If deleting sets, the rules must have been deleted prior to starting the * ipset session, so we can't delete the rules and the sets at the same time. */ #ifdef _HAVE_LIBIPSET_ if (h->cmd == IPADDRESS_ADD && h->session) { ipset_session_end(h->session); h->session = NULL; } #endif if (h->h4) res = ip4tables_close(h->h4, h->updated_v4); if (h->h6) res += ip6tables_close(h->h6, h->updated_v6); #ifdef _HAVE_LIBIPSET_ if (h->session) ipset_session_end(h->session); #endif FREE(h); return res; } static init_state_t check_chains_exist(uint8_t family) { struct iptc_handle *h4; struct ip6tc_handle *h6; init_state_t ret = INIT_SUCCESS; if (family == AF_INET) { h4 = ip4tables_open("filter"); if (!h4) { log_message(LOG_INFO, "WARNING, ip_tables module not installed - can't filter IPv4 addresses"); return INIT_FAILED; } if (global_data->vrrp_iptables_inchain && !ip4tables_is_chain(h4, global_data->vrrp_iptables_inchain)) { log_message(LOG_INFO, "iptables chain %s doesn't exist", global_data->vrrp_iptables_inchain); ret = INIT_FAILED; } if (global_data->vrrp_iptables_outchain && !ip4tables_is_chain(h4, global_data->vrrp_iptables_outchain)) { log_message(LOG_INFO, "iptables chain %s doesn't exist", global_data->vrrp_iptables_outchain); ret = INIT_FAILED; } ip4tables_close(h4, false); return ret; } h6 = ip6tables_open("filter"); if (!h6) { log_message(LOG_INFO, "WARNING, ip6_tables module not installed - can't filter IPv6 addresses"); return INIT_FAILED; } if (global_data->vrrp_iptables_inchain && !ip6tables_is_chain(h6, global_data->vrrp_iptables_inchain)) { log_message(LOG_INFO, "ip6tables chain %s doesn't exist", global_data->vrrp_iptables_inchain); ret = INIT_FAILED; } if (global_data->vrrp_iptables_outchain && !ip6tables_is_chain(h6, global_data->vrrp_iptables_outchain)) { log_message(LOG_INFO, "ip6tables chain %s doesn't exist", global_data->vrrp_iptables_outchain); ret = INIT_FAILED; } ip6tables_close(h6, false); return ret; } static void handle_iptable_rule_to_vip(ip_address_t *ipaddress, int cmd, struct ipt_handle *h, bool force) { char *ifname = NULL; uint8_t family = ipaddress->ifa.ifa_family; #ifdef _HAVE_LIBIPSET_ if (global_data->using_ipsets) { if (!h->session) h->session = ipset_session_start(); ipset_entry(h->session, cmd, ipaddress); ipaddress->iptable_rule_set = (cmd != IPADDRESS_DEL); return; } #endif if (family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&ipaddress->u.sin6_addr)) ifname = ipaddress->ifp->ifname; if (family == AF_INET6 && global_data->vrrp_iptables_inchain) { if (global_data->vrrp_iptables_outchain) { iptables_entry(h, AF_INET6, global_data->vrrp_iptables_outchain, APPEND_RULE, XTC_LABEL_ACCEPT, ipaddress, NULL, NULL, ifname, IPPROTO_ICMPV6, ND_NEIGHBOR_SOLICIT, cmd, 0, force); iptables_entry(h, AF_INET6, global_data->vrrp_iptables_outchain, APPEND_RULE, XTC_LABEL_ACCEPT, ipaddress, NULL, NULL, ifname, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, 0, force); } iptables_entry(h, AF_INET6, global_data->vrrp_iptables_inchain, APPEND_RULE, XTC_LABEL_ACCEPT, NULL, ipaddress, ifname, NULL, IPPROTO_ICMPV6, ND_NEIGHBOR_SOLICIT, cmd, 0, force); iptables_entry(h, AF_INET6, global_data->vrrp_iptables_inchain, APPEND_RULE, XTC_LABEL_ACCEPT, NULL, ipaddress, ifname, NULL, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, 0, force); } if (global_data->vrrp_iptables_outchain) iptables_entry(h, family, global_data->vrrp_iptables_outchain, APPEND_RULE, XTC_LABEL_DROP, ipaddress, NULL, NULL, ifname, IPPROTO_NONE, 0, cmd, 0, force); iptables_entry(h, family, global_data->vrrp_iptables_inchain, APPEND_RULE, XTC_LABEL_DROP, NULL, ipaddress, ifname, NULL, IPPROTO_NONE, 0, cmd, 0, force); ipaddress->iptable_rule_set = (cmd != IPADDRESS_DEL); } /* return true if a given file exists within procfs */ /* Taken from iptables code */ static bool proc_file_exists(const char *filename) { struct stat s; struct statfs f; if (lstat(filename, &s)) return false; if (!S_ISREG(s.st_mode)) return false; if (statfs(filename, &f)) return false; if (f.f_type != PROC_SUPER_MAGIC) return false; return true; } static bool iptables_init(int family) { if (family == AF_INET) { if (!proc_file_exists("/proc/net/ip_tables_names")) { log_message(LOG_INFO, "iptables chain not setup"); setup[family != AF_INET] = INIT_FAILED; return false; } } else { if (!proc_file_exists("/proc/net/ip6_tables_names")) { log_message(LOG_INFO, "ip6tables chain not setup"); setup[family != AF_INET] = INIT_FAILED; return false; } } #ifdef _LIBIPTC_DYNAMIC_ if (!iptables_lib_init(family)) { setup[family != AF_INET] = INIT_FAILED; return false; } #endif #ifdef _HAVE_LIBIPSET_ if (global_data->using_ipsets && !ipset_initialise()) global_data->using_ipsets = false; #endif setup[family != AF_INET] = check_chains_exist(family); return setup[family != AF_INET] == INIT_SUCCESS; } void iptables_fini(void) { #ifdef _HAVE_LIBIPSET_ uint8_t family; struct ipt_handle *h; if (!global_data->using_ipsets) return; h = iptables_open(IPADDRESS_DEL); family = AF_INET; do { if (vips_setup[family != AF_INET] == INIT_SUCCESS) add_del_vip_rules(h, IPADDRESS_DEL, family); #ifdef _HAVE_VRRP_VMAC_ if (igmp_setup[family != AF_INET] == INIT_SUCCESS) add_del_igmp_rules(h, IPADDRESS_DEL, family); #endif family = family == AF_INET ? AF_INET6 : 0; } while (family); iptables_close(h); /* The sets must not be in use when the ipset session starts */ h = iptables_open(IPADDRESS_DEL); family = AF_INET; do { if (vips_setup[family != AF_INET] == INIT_SUCCESS) { add_del_vip_sets(h, IPADDRESS_DEL, family); vips_setup[family != AF_INET] = NOT_INIT; } #ifdef _HAVE_VRRP_VMAC_ if (igmp_setup[family != AF_INET] == INIT_SUCCESS) add_del_igmp_sets(h, IPADDRESS_DEL, family); igmp_setup[family != AF_INET] = NOT_INIT; #endif family = family == AF_INET ? AF_INET6 : 0; } while (family); iptables_close(h); #endif } /* add/remove iptable drop rules to iplist */ static void handle_iptable_vip_list(struct ipt_handle *h, list_head_t *ip_list, int cmd, bool force) { ip_address_t *ipaddr; uint8_t family; list_for_each_entry(ipaddr, ip_list, e_list) { family = ipaddr->ifa.ifa_family; if (vips_setup[family != AF_INET] == NOT_INIT) { if (setup[family != AF_INET] == NOT_INIT) iptables_init(family); if (setup[family != AF_INET] == INIT_FAILED) { vips_setup[family != AF_INET] = INIT_FAILED; continue; } #ifdef _HAVE_LIBIPSET_ if (global_data->using_ipsets) { add_del_vip_sets(h, IPADDRESS_ADD, family); add_del_vip_rules(h, IPADDRESS_ADD, family); } #endif vips_setup[family != AF_INET] = INIT_SUCCESS; } if (vips_setup[family != AF_INET] == INIT_FAILED) continue; if ((cmd == IPADDRESS_DEL) == ipaddr->iptable_rule_set || force) handle_iptable_rule_to_vip(ipaddr, cmd, h, force); } } void handle_iptable_rule_to_iplist(list_head_t *ip_list1, list_head_t *ip_list2, int cmd, bool force) { struct ipt_handle *h; int tries = 0; int res = 0; /* No addresses in this list */ if ((!ip_list1 || list_empty(ip_list1)) && (!ip_list2 || list_empty(ip_list2))) return; do { h = iptables_open(cmd); if (ip_list1 && !list_empty(ip_list1)) handle_iptable_vip_list(h, ip_list1, cmd, force); if (ip_list2 && !list_empty(ip_list2)) handle_iptable_vip_list(h, ip_list2, cmd, force); res = iptables_close(h); } while (res == EAGAIN && ++tries < IPTABLES_MAX_TRIES); } void handle_iptables_accept_mode(vrrp_t *vrrp, int cmd, bool force) { handle_iptable_rule_to_iplist(&vrrp->vip, &vrrp->evip, cmd, force); } #ifdef _HAVE_VRRP_VMAC_ static void handle_iptable_rule_for_igmp(const char *ifname, int cmd, int family, struct ipt_handle *h) { if (!global_data->vrrp_iptables_outchain || igmp_setup[family != AF_INET] == INIT_FAILED) return; if (igmp_setup[family != AF_INET] == NOT_INIT) { if (setup[family != AF_INET] == NOT_INIT) iptables_init(family); if (setup[family != AF_INET] == INIT_FAILED) { igmp_setup[family != AF_INET] = INIT_FAILED; return; } #ifdef _HAVE_LIBIPSET_ if (global_data->using_ipsets) { add_del_igmp_sets(h, IPADDRESS_ADD, family); add_del_igmp_rules(h, IPADDRESS_ADD, family); } #endif igmp_setup[family != AF_INET] = INIT_SUCCESS; } #ifdef _HAVE_LIBIPSET_ if (global_data->using_ipsets) { if (!h->session) h->session = ipset_session_start(); ipset_entry_igmp(h->session, cmd, ifname, family); return; } #endif iptables_entry(h, family, global_data->vrrp_iptables_outchain, 0, XTC_LABEL_DROP, NULL, NULL, NULL, ifname, family == AF_INET ? IPPROTO_IGMP : IPPROTO_ICMPV6, family == AF_INET ? 0 : ICMPV6_MLD2_REPORT, cmd, 0, false); if (family == AF_INET6) iptables_entry(h, family, global_data->vrrp_iptables_outchain, 0, XTC_LABEL_DROP, NULL, NULL, NULL, ifname, IPPROTO_ICMPV6, MLD_LISTENER_REPORT, cmd, 0, false); } static void handle_iptable_rule_for_nd(const interface_t *ifp, int cmd, struct ipt_handle *h) { ip_address_t addr = { .ifa.ifa_family = AF_INET6, .u.sin6_addr = ifp->base_ifp->sin6_addr }; if (!global_data->vrrp_iptables_outchain || igmp_setup[1] == INIT_FAILED) return; if (igmp_setup[1] == NOT_INIT) { if (setup[1] == NOT_INIT) iptables_init(AF_INET6); if (setup[1] == INIT_FAILED) { igmp_setup[1] = INIT_FAILED; return; } #ifdef _HAVE_LIBIPSET_ if (global_data->using_ipsets) { add_del_igmp_sets(h, IPADDRESS_ADD, AF_INET6); add_del_igmp_rules(h, IPADDRESS_ADD, AF_INET6); } #endif igmp_setup[1] = INIT_SUCCESS; } #ifdef _HAVE_LIBIPSET_ if (global_data->using_ipsets) { if (!h->session) h->session = ipset_session_start(); ipset_entry_nd(h->session, cmd, ifp); return; } #endif iptables_entry(h, AF_INET6, global_data->vrrp_iptables_outchain, 0, XTC_LABEL_DROP, &addr, NULL, NULL, ifp->ifname, IPPROTO_ICMPV6, ND_NEIGHBOR_ADVERT, cmd, 0, false); } static void iptables_update_vmac(const interface_t *ifp, int family, bool other_family, int cmd) { struct ipt_handle *h; int tries = 0; int res = 0; do { h = iptables_open(cmd); handle_iptable_rule_for_igmp(ifp->ifname, cmd, family, h); if (other_family) handle_iptable_rule_for_igmp(ifp->ifname, cmd, family == AF_INET ? AF_INET6 : AF_INET, h); if (family == AF_INET6) handle_iptable_rule_for_nd(ifp, cmd, h); res = iptables_close(h); } while (res == EAGAIN && ++tries < IPTABLES_MAX_TRIES); } void iptables_add_vmac(const interface_t *ifp, int family, bool other_family) { iptables_update_vmac(ifp, family, other_family, IPADDRESS_ADD); } void iptables_remove_vmac(const interface_t *ifp, int family, bool other_family) { iptables_update_vmac(ifp, family, other_family, IPADDRESS_DEL); } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_ipsecah.c0000664000175000017500000000675214061601104015251 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: IPSEC AH implementation according to RFC 2402. Processing * authentication data encryption using HMAC MD5 according to * RFCs 2085 & 2104. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include "vrrp_ipsecah.h" #define BLOCK_SIZE 64 /* hmac_md5 computation according to the RFCs 2085 & 2104 */ void hmac_md5(const unsigned char *buffer1, size_t buffer1_len, const unsigned char *buffer2, size_t buffer2_len, const unsigned char *key, size_t key_len, unsigned char *digest) { EVP_MD_CTX *context; unsigned char k_ipad[BLOCK_SIZE+1]; /* inner padding - key XORd with ipad */ unsigned char k_opad[BLOCK_SIZE+1]; /* outer padding - key XORd with opad */ unsigned char tk[MD5_DIGEST_LENGTH]; int i; /* Initialize data */ memset(k_ipad, 0, sizeof (k_ipad)); memset(k_opad, 0, sizeof (k_opad)); memset(tk, 0, sizeof (tk)); /* If the key is longer than 64 bytes => set it to key=MD5(key) */ if (key_len > BLOCK_SIZE) { EVP_MD_CTX *tctx = EVP_MD_CTX_new(); /* Compute the MD5 digest */ EVP_DigestInit_ex(tctx, EVP_md5(), NULL); EVP_DigestUpdate(tctx, key, key_len); EVP_DigestFinal_ex(tctx, tk, NULL); key = tk; key_len = MD5_DIGEST_LENGTH; EVP_MD_CTX_free(tctx); } /* The global HMAC_MD5 algo looks like (rfc2085.2.2) : MD5(K XOR opad, MD5(K XOR ipad, buffer)) K : an n byte key ipad : byte 0x36 repeated 64 times opad : byte 0x5c repeated 64 times buffer : buffer being protected */ memset(k_ipad, 0, sizeof (k_ipad)); memset(k_opad, 0, sizeof (k_opad)); memcpy(k_ipad, key, key_len); memcpy(k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i = 0; i < BLOCK_SIZE; i++) { k_ipad[i] ^= 0x36; k_opad[i] ^= 0x5c; } /* Compute inner MD5 */ context = EVP_MD_CTX_new(); EVP_DigestInit_ex(context, EVP_md5(), NULL); /* Init context for 1st pass */ EVP_DigestUpdate(context, k_ipad, BLOCK_SIZE); /* start with inner pad */ EVP_DigestUpdate(context, buffer1, buffer1_len); /* next with buffer datagram */ if (buffer2) EVP_DigestUpdate(context, buffer2, buffer2_len); /* next with buffer datagram */ EVP_DigestFinal_ex(context, digest, NULL); /* Finish 1st pass */ /* Compute outer MD5 */ EVP_MD_CTX_reset(context); EVP_DigestInit_ex(context, EVP_md5(), NULL); /* Init context for 2nd pass */ EVP_DigestUpdate(context, k_opad, BLOCK_SIZE); /* start with inner pad */ EVP_DigestUpdate(context, digest, MD5_DIGEST_LENGTH); /* next result of 1st pass */ EVP_DigestFinal_ex(context, digest, NULL); /* Finish 2nd pass */ EVP_MD_CTX_free(context); } keepalived-2.3.3/keepalived/vrrp/vrrp_print.c0000664000175000017500000000637414714144150015001 /* * 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: Print running VRRP state information * * Author: John Southworth, * * 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) 2012 John Southworth, * Copyright (C) 2015-2017 Alexandre Cassen, */ #include "config.h" #include #include #include "logger.h" #include "list_head.h" #include "global_data.h" #include "vrrp.h" #include "vrrp_data.h" #include "vrrp_print.h" #include "utils.h" void vrrp_print_data(void) { FILE *fp; fp = open_dump_file(""); if (!fp) return; dump_data_vrrp(fp); fclose(fp); } void vrrp_print_stats(bool clear_stats) { FILE *file; vrrp_t *vrrp; const char *stats_file; stats_file = make_tmp_filename("keepalived.stats"); file = fopen_safe(stats_file, "w"); if (!file) { log_message(LOG_INFO, "Can't open %s (%d: %s)", stats_file, errno, strerror(errno)); FREE_CONST(stats_file); return; } FREE_CONST(stats_file); list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { fprintf(file, "VRRP Instance: %s\n", vrrp->iname); fprintf(file, " Advertisements:\n"); fprintf(file, " Received: %" PRIu64 "\n", vrrp->stats->advert_rcvd); fprintf(file, " Sent: %u\n", vrrp->stats->advert_sent); fprintf(file, " Became master: %u\n", vrrp->stats->become_master); fprintf(file, " Released master: %u\n", vrrp->stats->release_master); fprintf(file, " Packet Errors:\n"); fprintf(file, " Length: %" PRIu64 "\n", vrrp->stats->packet_len_err); fprintf(file, " TTL: %" PRIu64 "\n", vrrp->stats->ip_ttl_err); fprintf(file, " Invalid Type: %" PRIu64 "\n", vrrp->stats->invalid_type_rcvd); fprintf(file, " Advertisement Interval: %" PRIu64 "\n", vrrp->stats->advert_interval_err); fprintf(file, " Address List: %" PRIu64 "\n", vrrp->stats->addr_list_err); fprintf(file, " Authentication Errors:\n"); fprintf(file, " Invalid Type: %u\n", vrrp->stats->invalid_authtype); #ifdef _WITH_VRRP_AUTH_ fprintf(file, " Type Mismatch: %u\n", vrrp->stats->authtype_mismatch); fprintf(file, " Failure: %u\n", vrrp->stats->auth_failure); #endif fprintf(file, " Priority Zero:\n"); fprintf(file, " Received: %" PRIu64 "\n", vrrp->stats->pri_zero_rcvd); fprintf(file, " Sent: %" PRIu64 "\n", vrrp->stats->pri_zero_sent); if (clear_stats) memset(vrrp->stats, 0, sizeof(*vrrp->stats)); } fclose(file); } keepalived-2.3.3/keepalived/vrrp/vrrp_daemon.c0000664000175000017500000007560614771465436015134 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: VRRP child process handling. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef _WITH_PROFILING_ #include #endif #ifdef THREAD_DUMP #ifdef _WITH_SNMP_ #include "snmp.h" #endif #include "scheduler.h" #include "smtp.h" #include "vrrp_track.h" #endif #include "vrrp_daemon.h" #include "vrrp_scheduler.h" #include "vrrp_arp.h" #include "vrrp_ndisc.h" #include "keepalived_netlink.h" #include "vrrp_iprule.h" #include "vrrp_iproute.h" #include "vrrp_parser.h" #include "vrrp.h" #include "vrrp_print.h" #include "global_data.h" #include "pidfile.h" #include "logger.h" #include "signals.h" #include "process.h" #include "memory.h" #include "bitops.h" #include "rttables.h" #if defined _WITH_SNMP_RFC_ || defined _WITH_SNMP_VRRP_ #include "vrrp_snmp.h" #endif #ifdef _WITH_DBUS_ #include "vrrp_dbus.h" #endif #include "list_head.h" #include "main.h" #include "parser.h" #include "utils.h" #include "vrrp_notify.h" #include "track_file.h" #ifdef _WITH_JSON_ #include "vrrp_json.h" #endif #ifdef _WITH_BFD_ #include "bfd_daemon.h" #endif #ifdef _WITH_FIREWALL_ #include "vrrp_firewall.h" #endif #ifdef _WITH_TRACK_PROCESS_ #include "track_process.h" #endif #ifdef _WITH_LVS_ #include "ipvswrapper.h" #endif #ifdef _USE_SYSTEMD_NOTIFY_ #include "systemd.h" #endif #ifndef _ONE_PROCESS_DEBUG_ #include "config_notify.h" #endif /* Global variables */ bool non_existent_interface_specified; const char * const igmp_link_local_mcast_reports = "/proc/sys/net/ipv4/igmp_link_local_mcast_reports"; /* Forward declarations */ #ifndef _ONE_PROCESS_DEBUG_ static void print_vrrp_data(thread_ref_t); static void print_vrrp_stats(thread_ref_t); static void reload_vrrp_thread(thread_ref_t); #ifdef _WITH_JSON_ static void print_vrrp_json(thread_ref_t); #endif #endif #ifdef _WITH_PERF_ perf_t perf_run = PERF_NONE; #endif /* local variables */ static const char *vrrp_syslog_ident; static char sav_igmp_link_local_mcast_reports; #ifndef _ONE_PROCESS_DEBUG_ static bool two_phase_terminate; static timeval_t vrrp_start_time; static unsigned vrrp_next_restart_delay; #endif #ifdef _VRRP_FD_DEBUG_ bool do_vrrp_fd_debug; #endif #ifndef _ONE_PROCESS_DEBUG_ #ifdef _VRRP_FD_DEBUG_ static void dump_vrrp_fd(void) { sock_t *sock; vrrp_t *vrrp; timeval_t time_diff; log_message(LOG_INFO, "----[ Begin VRRP fd dump ]----"); list_for_each_entry(sock, &vrrp_data->vrrp_socket_pool, e_list) { log_message(LOG_INFO, " Sockets %d, %d", sock->fd_in, sock->fd_out); rb_for_each_entry_cached(vrrp, &sock->rb_sands, rb_sands) { if (vrrp->sands.tv_sec == TIMER_DISABLED) log_message(LOG_INFO, " %s: sands DISABLED", vrrp->iname); else { timersub(&vrrp->sands, &time_now, &time_diff); if (time_diff.tv_sec >= 0) log_message(LOG_INFO, " %s: sands %" PRI_tv_sec ".%6.6" PRI_tv_usec, vrrp->iname, time_diff.tv_sec, time_diff.tv_usec); else log_message(LOG_INFO, " %s: sands -%" PRI_tv_sec ".%6.6" PRI_tv_usec, vrrp->iname, -time_diff.tv_sec - (time_diff.tv_usec ? 1 : 0), time_diff.tv_usec ? 1000000 - time_diff.tv_usec : 0); } } rb_for_each_entry(vrrp, &sock->rb_vrid, rb_vrid) log_message(LOG_INFO, " %s: vrid %d", vrrp->iname, vrrp->vrid); } log_message(LOG_INFO, "----[ End VRRP fd dump ]----"); } #endif #endif static void set_vrrp_max_fds(void) { vrrp_t *vrrp; int cnt = 0; if (list_empty(&vrrp_data->vrrp)) return; /* This is called at boot so ok performing full walk */ list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) cnt++; /* Allow: * 2 per vrrp instance - always needed for VMAC instances * * plus: * * stdin/stdout/stderr * logger * logger file * timer fd * inotify fd * signal fd * epoll fd * 3 for SNMP * 2 for netlink * bfd pipe * 2 * notify fifo pipes * track_file (only one open at a time) * mem_check file * USR1/USR2/JSON data * smtp-alert file * memfd for config * eventfd for notifying load/reload complete * * plus: * * 20 spare (in case we have forgotten anything) */ set_max_file_limit(cnt * 2 + vrrp_data->num_smtp_alert + 23 + 20); } #ifdef _WITH_LVS_ static bool vrrp_ipvs_needed(void) { return global_data->lvs_syncd.ifname && (global_data->lvs_syncd.vrrp || global_data->lvs_syncd.vrrp_name); } #endif static void set_disable_local_igmp(void) { char buf; int fd; ssize_t len; if ((fd = open(igmp_link_local_mcast_reports, O_RDWR)) == -1) { log_message(LOG_INFO, "Unable to open %s - errno %d", igmp_link_local_mcast_reports, errno); global_data->disable_local_igmp = false; return; } if ((len = read(fd, &sav_igmp_link_local_mcast_reports, 1)) != 1) { log_message(LOG_INFO, "Unable to read %s - errno %d", igmp_link_local_mcast_reports, errno); global_data->disable_local_igmp = false; close(fd); return; } if (sav_igmp_link_local_mcast_reports == '1') { buf = '0'; lseek(fd, 0, SEEK_SET); if (write(fd, &buf, 1) != 1) { log_message(LOG_INFO, "Unable to write %s - errno %d", igmp_link_local_mcast_reports, errno); global_data->disable_local_igmp = false; close(fd); return; } } close(fd); } static void reset_disable_local_igmp(void) { int fd; if (sav_igmp_link_local_mcast_reports == '1') { fd = open(igmp_link_local_mcast_reports, O_RDWR); if (fd == -1 || write(fd, &sav_igmp_link_local_mcast_reports, 1) == -1) log_message(LOG_INFO, "Unable to write %s - errno %d", igmp_link_local_mcast_reports, errno); if (fd != -1) close(fd); } } static int vrrp_terminate_phase2(int exit_status) { #ifdef _NETLINK_TIMERS_ if (do_netlink_timers) report_and_clear_netlink_timers("Starting shutdown instances"); #endif if (!__test_bit(DONT_RELEASE_VRRP_BIT, &debug)) shutdown_vrrp_instances(); #ifdef _NETLINK_TIMERS_ if (do_netlink_timers) report_and_clear_netlink_timers("Completed shutdown instances"); #endif #if defined _WITH_SNMP_RFC_ || defined _WITH_SNMP_VRRP_ if ( #ifdef _WITH_SNMP_VRRP_ global_data->enable_snmp_vrrp || #endif #ifdef _WITH_SNMP_RFCV2_ global_data->enable_snmp_rfcv2 || #endif #ifdef _WITH_SNMP_RFCV3_ global_data->enable_snmp_rfcv3 || #endif false) vrrp_snmp_agent_close(global_data); #endif #ifdef _WITH_LVS_ if (vrrp_ipvs_needed()) { /* Clean ipvs related */ ipvs_stop(); } #endif #ifdef _WITH_FIREWALL_ firewall_fini(); #endif kernel_netlink_close_cmd(); thread_destroy_master(master); master = NULL; gratuitous_arp_close(); ndisc_close(); #ifdef _WITH_LINKBEAT_ close_interface_linkbeat(); #endif #ifdef _WITH_DBUS_ if (global_data->enable_dbus) dbus_stop(); #endif clear_rt_names(); if (global_data->vrrp_notify_fifo.fd != -1) notify_fifo_close(&global_data->notify_fifo, &global_data->vrrp_notify_fifo); if (global_data->disable_local_igmp) reset_disable_local_igmp(); free_global_data(&global_data); free_vrrp_data(&vrrp_data); free_vrrp_buffer(); free_interface_queue(); free_parent_mallocs_exit(); /* * Reached when terminate signal catched. * finally return to parent process. */ log_stopping(); #ifdef ENABLE_LOG_TO_FILE if (log_file_name) close_log_file(); #endif closelog(); #ifndef _MEM_CHECK_LOG_ FREE_CONST_PTR(vrrp_syslog_ident); #else if (vrrp_syslog_ident) free(no_const_char_p(vrrp_syslog_ident)); /* malloc'd by make_syslog_ident() */ #endif close_std_fd(); /* Stop daemon */ pidfile_rm(&vrrp_pidfile); exit(exit_status); } static void vrrp_shutdown_backstop_thread(thread_ref_t thread) { int count = 0; thread_ref_t t; /* Force terminate all script processes */ if (thread->master->child.rb_root.rb_node) script_killall(thread->master, SIGKILL, true); rb_for_each_entry_cached_const(t, &thread->master->child, n) count++; log_message(LOG_ERR, "Backstop thread invoked: shutdown timer %srunning, child count %d", thread->master->shutdown_timer_running ? "" : "not ", count); thread_add_terminate_event(thread->master); } static void vrrp_shutdown_timer_thread(thread_ref_t thread) { thread->master->shutdown_timer_running = false; if (thread->master->child.rb_root.rb_node) thread_add_timer_shutdown(thread->master, vrrp_shutdown_backstop_thread, NULL, TIMER_HZ / 10); else thread_add_terminate_event(thread->master); } /* Daemon stop sequence */ static void vrrp_terminate_phase1(bool schedule_next_thread) { #ifdef _WITH_PERF_ if (perf_run == PERF_END) run_perf("vrrp", global_data->network_namespace, global_data->instance_name); #endif #ifdef _WITH_TRACK_PROCESS_ /* Stop monitoring process terminations */ end_process_monitor(); #endif /* Terminate all script processes */ if (master->child.rb_root.rb_node) script_killall(master, SIGTERM, true); kernel_netlink_close_monitor(); #ifdef _NETLINK_TIMERS_ if (do_netlink_timers) report_and_clear_netlink_timers("Start shutdown"); #endif #ifdef _WITH_LVS_ if (vrrp_ipvs_needed()) { /* Stop syncd if controlled by this vrrp process. */ ipvs_syncd_cmd(IPVS_STOPDAEMON, &global_data->lvs_syncd, global_data->lvs_syncd.vrrp->state == VRRP_STATE_MAST ? IPVS_MASTER: IPVS_BACKUP, false); } #endif /* Ensure any interfaces are in backup mode, * sending a priority 0 vrrp message */ if (!__test_bit(DONT_RELEASE_VRRP_BIT, &debug)) restore_vrrp_interfaces(); #ifdef _NETLINK_TIMERS_ if (do_netlink_timers) report_and_clear_netlink_timers("Restored interfaces"); #endif if (!list_empty(&vrrp_data->vrrp_track_files)) stop_track_files(); /* Clear static entries */ netlink_rulelist(&vrrp_data->static_rules, IPRULE_DEL, false); netlink_rtlist(&vrrp_data->static_routes, IPROUTE_DEL, false); netlink_iplist(&vrrp_data->static_addresses, IPADDRESS_DEL, false); #ifdef _NETLINK_TIMERS_ if (do_netlink_timers) report_and_clear_netlink_timers("Static addresses/routes/rules cleared"); #endif /* Clean data */ vrrp_dispatcher_release(vrrp_data); /* Send shutdown notifications */ notify_shutdown(); if (schedule_next_thread) { if (!list_empty(&vrrp_data->vrrp)) { /* This is not nice, but it significantly increases the chances * of an IGMP leave group being sent for some reason. * Since we are about to exit, it doesn't affect anything else * running. */ thread_add_timer_shutdown(master, vrrp_shutdown_timer_thread, NULL, TIMER_HZ); master->shutdown_timer_running = true; } else if (master->child.rb_root.rb_node) { /* Add a backstop timer for the shutdown */ thread_add_timer_shutdown(master, vrrp_shutdown_backstop_thread, NULL, TIMER_HZ); } else thread_add_terminate_event(master); } } #ifndef _ONE_PROCESS_DEBUG_ static void start_vrrp_termination_thread(__attribute__((unused)) thread_ref_t thread) { /* This runs in the context of a thread */ two_phase_terminate = true; vrrp_terminate_phase1(true); } #endif /* Daemon stop sequence */ static void stop_vrrp(int status) { if (__test_bit(CONFIG_TEST_BIT, &debug)) return; /* This runs in the main process, not in the context of a thread */ vrrp_terminate_phase1(false); vrrp_terminate_phase2(status); /* unreachable */ exit(status); } /* Daemon init sequence */ static void delayed_start_clear_thread(__attribute__((unused)) thread_ref_t thread) { vrrp_delayed_start_time.tv_sec = 0; log_message(LOG_INFO, "Delayed start completed"); } static void start_vrrp(data_t *prev_global_data) { unsigned delay_remaining; /* Clear the flags used for optimising performance */ clear_summary_flags(); /* Initialize sub-system */ if (!__test_bit(CONFIG_TEST_BIT, &debug)) kernel_netlink_init(); if (!global_data) global_data = alloc_global_data(); /* Parse configuration file */ vrrp_data = alloc_vrrp_data(); if (!vrrp_data) { stop_vrrp(KEEPALIVED_EXIT_FATAL); return; } init_data(conf_file, vrrp_init_keywords, false); /* Update process name if necessary */ if ((!prev_global_data && // startup global_data->vrrp_process_name) || (prev_global_data && // reload (!global_data->vrrp_process_name != !prev_global_data->vrrp_process_name || (global_data->vrrp_process_name && strcmp(global_data->vrrp_process_name, prev_global_data->vrrp_process_name))))) set_process_name(global_data->vrrp_process_name); if (non_existent_interface_specified) { report_config_error(CONFIG_BAD_IF, "Non-existent interface specified in configuration"); stop_vrrp(KEEPALIVED_EXIT_CONFIG); return; } if (reload) init_global_data(global_data, prev_global_data, true); /* Set our copy of time */ set_time_now(); if (!__test_bit(CONFIG_TEST_BIT, &debug)) { #if defined _WITH_SNMP_RFC_ || defined _WITH_SNMP_VRRP_ if ( #ifdef _WITH_SNMP_VRRP_ global_data->enable_snmp_vrrp || #endif #ifdef _WITH_SNMP_RFCV2_ global_data->enable_snmp_rfcv2 || #endif #ifdef _WITH_SNMP_RFCV3_ global_data->enable_snmp_rfcv3 || #endif false) { if (snmp_running) snmp_epoll_info(master); else vrrp_snmp_agent_init(global_data->snmp_socket); #ifdef _WITH_SNMP_RFC_ snmp_vrrp_start_time = time_now; #endif } else { // We have a problem at reload if VRRP had SNMP and checker didn't, but now checker does. // Also race condition if changing so checker does and we dont, from other way round. // SOLUTION: Stop snmp before reload and start afterwards. ? A race anyway if (snmp_running) vrrp_snmp_agent_close(old_global_data); } #endif #ifdef _WITH_LVS_ if (vrrp_ipvs_needed()) { /* Initialize ipvs related */ if (ipvs_start() != IPVS_SUCCESS) { stop_vrrp(KEEPALIVED_EXIT_FATAL); return; } } #endif if (reload) { kernel_netlink_set_recv_bufs(); clear_diff_static_rules(); clear_diff_static_routes(); clear_diff_static_addresses(); clear_diff_script(); #ifdef _WITH_BFD_ clear_diff_bfd(); #endif } else { /* Clear leftover static entries */ netlink_iplist(&vrrp_data->static_addresses, IPADDRESS_DEL, false); netlink_rtlist(&vrrp_data->static_routes, IPROUTE_DEL, false); netlink_error_ignore = ENOENT; netlink_rulelist(&vrrp_data->static_rules, IPRULE_DEL, true); netlink_error_ignore = 0; } } if (!__test_bit(CONFIG_TEST_BIT, &debug)) { /* Init & start the VRRP packet dispatcher */ thread_add_event(master, vrrp_dispatcher_init, NULL, 0); if (!reload) { if (global_data->vrrp_startup_delay) { vrrp_delayed_start_time = timer_add_long(time_now, global_data->vrrp_startup_delay); thread_add_timer(master, delayed_start_clear_thread, NULL, global_data->vrrp_startup_delay); log_message(LOG_INFO, "Delaying startup for %g seconds", global_data->vrrp_startup_delay / TIMER_HZ_DOUBLE); } else vrrp_delayed_start_time.tv_sec = 0; } else if (vrrp_delayed_start_time.tv_sec) { vrrp_delayed_start_time = timer_sub_long(vrrp_delayed_start_time, prev_global_data->vrrp_startup_delay); vrrp_delayed_start_time = timer_add_long(vrrp_delayed_start_time, global_data->vrrp_startup_delay); if (time_now.tv_sec > vrrp_delayed_start_time.tv_sec || (time_now.tv_sec == vrrp_delayed_start_time.tv_sec && time_now.tv_usec >= vrrp_delayed_start_time.tv_usec)) { vrrp_delayed_start_time.tv_sec = 0; log_message(LOG_INFO, "Reduced delayed start passed"); } else { delay_remaining = (vrrp_delayed_start_time.tv_sec - time_now.tv_sec) * 1000000UL + vrrp_delayed_start_time.tv_usec - time_now.tv_usec; thread_add_timer(master, delayed_start_clear_thread, NULL, delay_remaining); log_message(LOG_INFO, "Delaying startup for a further %g seconds", delay_remaining / TIMER_HZ_DOUBLE); } } if (!reload && global_data->disable_local_igmp) set_disable_local_igmp(); } /* Complete VRRP initialization */ if (!vrrp_complete_init() #ifndef _ONE_PROCESS_DEBUG_ || (global_data->reload_check_config && get_config_status() != CONFIG_OK) #endif ) { stop_vrrp(KEEPALIVED_EXIT_CONFIG); return; } /* If we are just testing the configuration, then we terminate now */ if (__test_bit(CONFIG_TEST_BIT, &debug)) return; /* Start or stop gratuitous arp/ndisc as appropriate */ if (have_ipv4_instance) { if (!gratuitous_arp_init()) stop_vrrp(KEEPALIVED_EXIT_MISSING_PERMISSION); } else gratuitous_arp_close(); if (have_ipv6_instance) { if (!ndisc_init()) stop_vrrp(KEEPALIVED_EXIT_MISSING_PERMISSION); } else ndisc_close(); #ifndef _ONE_PROCESS_DEBUG_ /* Notify parent config has been read if appropriate */ if (!__test_bit(CONFIG_TEST_BIT, &debug)) notify_config_read(); #endif if (!reload) vrrp_restore_interfaces_startup(); /* clear_diff_vrrp must be called after vrrp_complete_init, since the latter * sets ifp on the addresses, which is used for the address comparison */ if (reload) { clear_diff_vrrp(); vrrp_dispatcher_release(old_vrrp_data); /* Set previous sync group states to suppress duplicate notifies */ set_previous_sync_group_states(); } #ifdef _WITH_DBUS_ if (global_data->enable_dbus) { if (reload && old_global_data->enable_dbus) dbus_reload(&old_vrrp_data->vrrp, &vrrp_data->vrrp); else { if (!dbus_start()) global_data->enable_dbus = false; } } else if (reload && old_global_data->enable_dbus) dbus_stop(); #endif /* Set static entries */ netlink_iplist(&vrrp_data->static_addresses, IPADDRESS_ADD, false); netlink_rtlist(&vrrp_data->static_routes, IPROUTE_ADD, false); netlink_rulelist(&vrrp_data->static_rules, IPRULE_ADD, false); /* Dump configuration */ if (__test_bit(DUMP_CONF_BIT, &debug)) dump_data_vrrp(NULL); /* Set the process priority and non swappable if configured */ set_process_priorities(global_data->vrrp_realtime_priority, global_data->max_auto_priority, global_data->min_auto_priority_delay, global_data->vrrp_rlimit_rt, global_data->vrrp_process_priority, global_data->vrrp_no_swap ? 4096 : 0); /* Set the process cpu affinity if configured */ set_process_cpu_affinity(&global_data->vrrp_cpu_mask, "vrrp"); /* Ensure we can open sufficient file descriptors */ set_vrrp_max_fds(); } #ifndef _ONE_PROCESS_DEBUG_ static void send_reload_advert_thread(thread_ref_t thread) { vrrp_t *vrrp = THREAD_ARG(thread); if (vrrp->state == VRRP_STATE_MAST) vrrp_send_adv(vrrp, vrrp->effective_priority); /* If this is the last vrrp instance to send an advert, schedule the * actual reload. */ if (THREAD_VAL(thread)) thread_add_event(master, reload_vrrp_thread, NULL, 0); } static void sigreload_vrrp(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { vrrp_t *vrrp; int num_master_inst = 0; int i = 0; /* We want to send adverts for the vrrp instances which are * in master state. After that the reload can be initiated */ if (!list_empty(&vrrp_data->vrrp)) { list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->state == VRRP_STATE_MAST) num_master_inst++; } list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { if (vrrp->state == VRRP_STATE_MAST) { i++; thread_add_event(master, send_reload_advert_thread, vrrp, i == num_master_inst); } } } if (num_master_inst == 0) thread_add_event(master, reload_vrrp_thread, NULL, 0); } static void sigusr1_vrrp(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { log_message(LOG_INFO, "Printing VRRP data for process(%d) on signal", getpid()); thread_add_event(master, print_vrrp_data, NULL, 0); } static void sigusr2_vrrp(__attribute__((unused)) void *v, int sig) { log_message(LOG_INFO, "Printing %sVRRP stats for process(%d) on signal", sig == SIGSTATS_CLEAR ? "and clearing " : "", getpid()); thread_add_event(master, print_vrrp_stats, NULL, sig); } #ifdef _WITH_JSON_ static void sigjson_vrrp(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { thread_add_event(master, print_vrrp_json, NULL, 0); } #endif /* Terminate handler */ static void sigend_vrrp(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { if (master) thread_add_start_terminate_event(master, start_vrrp_termination_thread); } /* VRRP Child signal handling */ static void vrrp_signal_init(void) { signal_set(SIGHUP, sigreload_vrrp, NULL); if (ignore_sigint) signal_ignore(SIGINT); else signal_set(SIGINT, sigend_vrrp, NULL); signal_set(SIGTERM, sigend_vrrp, NULL); signal_set(SIGUSR1, sigusr1_vrrp, NULL); signal_set(SIGUSR2, sigusr2_vrrp, NULL); signal_set(SIGSTATS_CLEAR, sigusr2_vrrp, NULL); #ifdef _WITH_JSON_ signal_set(SIGJSON, sigjson_vrrp, NULL); #endif #ifdef THREAD_DUMP signal_set(SIGTDUMP, thread_dump_signal, NULL); #endif signal_ignore(SIGPIPE); } /* Reload thread */ static void reload_vrrp_thread(__attribute__((unused)) thread_ref_t thread) { bool with_snmp = false; #ifdef _WITH_LVS_ bool want_syncd_master; #endif log_message(LOG_INFO, "Reloading"); /* Use standard scheduling while reloading */ reset_process_priorities(); #ifndef _ONE_PROCESS_DEBUG_ save_config(false, "vrrp", dump_data_vrrp); #endif reinitialise_global_vars(); /* set the reloading flag */ SET_RELOAD; /* Terminate all script process */ script_killall(master, SIGTERM, false); if (!list_empty(&vrrp_data->vrrp_track_files)) stop_track_files(); vrrp_initialised = false; #if !defined _ONE_PROCESS_DEBUG_ && defined _WITH_SNMP_VRRP_ if ( #ifdef _WITH_SNMP_VRRP_ global_data->enable_snmp_vrrp || #endif #ifdef _WITH_SNMP_RFCV2_ global_data->enable_snmp_rfcv2 || #endif #ifdef _WITH_SNMP_RFCV3_ global_data->enable_snmp_rfcv3 || #endif false) with_snmp = true; #endif /* Destroy master thread */ #ifdef _WITH_BFD_ cancel_vrrp_threads(); #endif cancel_kernel_netlink_threads(); thread_cleanup_master(master, true); thread_add_base_threads(master, with_snmp); /* Remove the notify fifo - we don't know if it will be the same after a reload */ notify_fifo_close(&global_data->notify_fifo, &global_data->vrrp_notify_fifo); #ifdef _WITH_LVS_ if (vrrp_ipvs_needed()) { /* Clean ipvs related */ ipvs_stop(); } #endif /* Save previous conf data */ old_vrrp_data = vrrp_data; vrrp_data = NULL; old_global_data = global_data; global_data = NULL; reset_interface_queue(); reset_next_rule_priority(); /* Reload the conf */ start_vrrp(old_global_data); #ifdef _WITH_LVS_ if (vrrp_ipvs_needed()) { /* coverity[var_deref_op] */ want_syncd_master = (global_data->lvs_syncd.vrrp->state == VRRP_STATE_MAST); if (ipvs_syncd_changed(&old_global_data->lvs_syncd, &global_data->lvs_syncd)) ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, want_syncd_master ? IPVS_MASTER : IPVS_BACKUP, true); ipvs_syncd_cmd(IPVS_STARTDAEMON, NULL, want_syncd_master ? IPVS_MASTER : IPVS_BACKUP, true); ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, want_syncd_master ? IPVS_BACKUP : IPVS_MASTER, true); } #endif /* free backup data */ free_vrrp_data(&old_vrrp_data); free_global_data(&old_global_data); free_old_interface_queue(); #ifndef _ONE_PROCESS_DEBUG_ save_config(true, "vrrp", dump_data_vrrp); #endif /* Post initializations */ #ifdef _MEM_CHECK_ log_message(LOG_INFO, "Configuration is using : %zu Bytes", get_keepalived_cur_mem_allocated()); #endif } static void print_vrrp_data(__attribute__((unused)) thread_ref_t thread) { vrrp_print_data(); } static void print_vrrp_stats(thread_ref_t thread) { vrrp_print_stats(thread->u.val == SIGSTATS_CLEAR); } #ifdef _WITH_JSON_ static void print_vrrp_json(__attribute__((unused)) thread_ref_t thread) { vrrp_print_json(); } #endif /* This function runs in the parent process. */ static void delayed_restart_vrrp_child_thread(__attribute__((unused)) thread_ref_t thread) { start_vrrp_child(); } /* VRRP Child respawning thread. This function runs in the parent process. */ static void vrrp_respawn_thread(thread_ref_t thread) { unsigned restart_delay; int ret; /* We catch a SIGCHLD, handle it */ vrrp_child = 0; if ((ret = report_child_status(thread->u.c.status, thread->u.c.pid, NULL))) thread_add_parent_terminate_event(thread->master, ret); else if (!__test_bit(DONT_RESPAWN_BIT, &debug)) { log_child_died("VRRP", thread->u.c.pid); restart_delay = calc_restart_delay(&vrrp_start_time, &vrrp_next_restart_delay, "VRRP"); if (!restart_delay) start_vrrp_child(); else thread_add_timer(thread->master, delayed_restart_vrrp_child_thread, NULL, restart_delay * TIMER_HZ); } else { log_message(LOG_ALERT, "VRRP child process(%d) died: Exiting", thread->u.c.pid); raise(SIGTERM); } } #endif #ifdef THREAD_DUMP static void register_vrrp_thread_addresses(void) { register_scheduler_addresses(); register_signal_thread_addresses(); register_notify_addresses(); register_smtp_addresses(); register_keepalived_netlink_addresses(); #ifdef _WITH_SNMP_ register_snmp_addresses(); #endif register_vrrp_if_addresses(); register_vrrp_scheduler_addresses(); #ifdef _WITH_DBUS_ register_vrrp_dbus_addresses(); #endif register_vrrp_fifo_addresses(); register_track_file_inotify_addresses(); #ifdef _WITH_TRACK_PROCESS_ register_process_monitor_addresses(); #endif #ifndef _ONE_PROCESS_DEBUG_ register_thread_address("print_vrrp_data", print_vrrp_data); register_thread_address("print_vrrp_stats", print_vrrp_stats); register_thread_address("reload_vrrp_thread", reload_vrrp_thread); register_thread_address("start_vrrp_termination_thread", start_vrrp_termination_thread); register_thread_address("send_reload_advert_thread", send_reload_advert_thread); #endif register_thread_address("delayed_start_clear_thread", delayed_start_clear_thread); register_thread_address("vrrp_shutdown_backstop_thread", vrrp_shutdown_backstop_thread); register_thread_address("vrrp_shutdown_timer_thread", vrrp_shutdown_timer_thread); #ifndef _ONE_PROCESS_DEBUG_ register_signal_handler_address("sigreload_vrrp", sigreload_vrrp); register_signal_handler_address("sigend_vrrp", sigend_vrrp); register_signal_handler_address("sigusr1_vrrp", sigusr1_vrrp); register_signal_handler_address("sigusr2_vrrp", sigusr2_vrrp); #ifdef _WITH_JSON_ register_signal_handler_address("sigjson_vrrp", sigjson_vrrp); #endif #ifdef THREAD_DUMP register_signal_handler_address("thread_dump_signal", thread_dump_signal); #endif #endif } #endif /* Register VRRP thread */ int start_vrrp_child(void) { #ifndef _ONE_PROCESS_DEBUG_ pid_t pid; const char *syslog_ident; /* Initialize child process */ #ifdef ENABLE_LOG_TO_FILE if (log_file_name) flush_log_file(); #endif pid = fork(); if (pid < 0) { log_message(LOG_INFO, "VRRP child process: fork error(%s)" , strerror(errno)); return -1; } else if (pid) { vrrp_child = pid; vrrp_start_time = time_now; log_message(LOG_INFO, "Starting VRRP child process, pid=%d" , pid); /* Start respawning thread */ thread_add_child(master, vrrp_respawn_thread, NULL, pid, TIMER_NEVER); return 0; } #ifdef _WITH_PROFILING_ /* See https://lists.gnu.org/archive/html/bug-gnu-utils/2001-09/msg00047.html for details */ monstartup ((u_long) &_start, (u_long) &etext); #endif prctl(PR_SET_PDEATHSIG, SIGTERM); /* Check our parent hasn't already changed since the fork */ if (main_pid != getppid()) kill(getpid(), SIGTERM); #ifdef _WITH_PERF_ if (perf_run == PERF_ALL) run_perf("vrrp", global_data->network_namespace, global_data->instance_name); #endif prog_type = PROG_TYPE_VRRP; initialise_debug_options(); #ifdef THREAD_DUMP /* Remove anything we might have inherited from parent */ deregister_thread_addresses(); #endif close_other_pidfiles(); #ifdef _WITH_BFD_ /* Close the write end of the BFD vrrp event notification pipe */ close(bfd_vrrp_event_pipe[1]); #ifdef _WITH_LVS_ close(bfd_checker_event_pipe[0]); close(bfd_checker_event_pipe[1]); #endif #endif /* Opening local VRRP syslog channel */ if ((global_data->instance_name || global_data->network_namespace) && (vrrp_syslog_ident = make_syslog_ident(PROG_VRRP))) syslog_ident = vrrp_syslog_ident; else syslog_ident = PROG_VRRP; if (!__test_bit(NO_SYSLOG_BIT, &debug)) open_syslog(syslog_ident); #ifdef ENABLE_LOG_TO_FILE if (log_file_name) open_log_file(log_file_name, "vrrp", global_data->network_namespace, global_data->instance_name); #endif #ifdef _MEM_CHECK_ mem_log_init(PROG_VRRP, "VRRP Child process"); #endif free_parent_mallocs_startup(true); /* Clear any child finder functions set in parent */ set_child_finder_name(NULL); /* Create an independant file descriptor for the shared config file */ separate_config_file(); /* Child process part, write pidfile */ if (!pidfile_write(&vrrp_pidfile)) { /* Fatal error */ log_message(LOG_INFO, "VRRP child process: cannot write pidfile"); exit(0); } #ifdef _USE_SYSTEMD_NOTIFY_ 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); #endif /* Create the new master thread */ thread_destroy_master(master); /* This destroys any residual settings from the parent */ master = thread_make_master(); #endif /* If last process died during a reload, we can get there and we * don't want to loop again, because we're not reloading anymore. */ UNSET_RELOAD; #ifndef _ONE_PROCESS_DEBUG_ /* Signal handling initialization */ vrrp_signal_init(); /* Register emergency shutdown function */ register_shutdown_function(stop_vrrp); #endif /* Start VRRP daemon */ start_vrrp(NULL); #ifdef _ONE_PROCESS_DEBUG_ return 0; #endif #ifdef THREAD_DUMP register_vrrp_thread_addresses(); #endif /* Post initializations */ #ifdef _MEM_CHECK_ /* Note: there may be a proc_events_ack_timer thread which will not * exist when the same configuration is reloaded. This is a thread_t, * which currently adds 120 bytes to the allocated memory. */ log_message(LOG_INFO, "Configuration is using : %zu Bytes", get_keepalived_cur_mem_allocated()); #endif #ifdef _WITH_PERF_ if (perf_run == PERF_RUN) run_perf("vrrp", global_data->network_namespace, global_data->instance_name); #endif /* Launch the scheduling I/O multiplexer */ launch_thread_scheduler(master); #ifdef THREAD_DUMP deregister_thread_addresses(); #endif /* Finish VRRP daemon process */ vrrp_terminate_phase2(EXIT_SUCCESS); /* unreachable */ exit(EXIT_SUCCESS); } void vrrp_validate_config(void) { start_vrrp(NULL); } #ifdef THREAD_DUMP void register_vrrp_parent_addresses(void) { #ifndef _ONE_PROCESS_DEBUG_ register_thread_address("vrrp_respawn_thread", vrrp_respawn_thread); register_thread_address("delayed_restart_vrrp_child_thread", delayed_restart_vrrp_child_thread); #endif } #endif keepalived-2.3.3/keepalived/vrrp/vrrp_firewall.c0000664000175000017500000001270314134543436015451 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: accept mode management * * Author: Alexandre Cassen, * * 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-2018 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include "vrrp_firewall.h" #ifdef _WITH_IPTABLES_ #include "vrrp_iptables.h" #endif #ifdef _WITH_NFTABLES_ #include "vrrp_nftables.h" #endif #include "global_data.h" #include "vrrp_ipaddress.h" #include "utils.h" #include "bitops.h" #include "logger.h" #if defined _WITH_IPTABLES_ && defined _WITH_NFTABLES_ static bool checked_iptables_nft; static void check_iptables_nft(void) { FILE *fp; char buf[40]; size_t len; char *res; /* Increasingly the iptables command is being provided as a front end to nftables. If so, * then if we are built with nftables support, we should use nftables. */ checked_iptables_nft = true; /* If using iptables is not configured, we don't need to do anything */ if (!global_data->vrrp_iptables_inchain && !global_data->vrrp_iptables_outchain) return; fp = popen("iptables -V", "r"); if (!fp) { /* No iptables command, so we need to use nftables */ log_message(LOG_INFO, "Using nftables since no iptables command found - please update configuration"); } else { res = fgets(buf, sizeof buf, fp); pclose(fp); if (!res) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "popen(\"iptables -V\" read failed - errno %d - %m", errno); return; } /* iptables will either have no type, or the type will be "nf_tables" or "legacy" */ if ((len = strlen(buf)) && buf[len-1] == '\n') buf[--len] = '\0'; if (len <= 10 || buf[len-1] != ')') return; /* If the type is not nf_tables, then iptables command is creating iptables configuration */ if (strncmp(buf + len - 1 - 9, "nf_tables", 9)) return; #ifdef ALLOW_IPTABLES_LEGACY fp = popen("iptables-legacy -V", "r"); if (fp) { fclose(fp); /* The iptables-legacy command exists, so can use iptables */ return; } #endif log_message(LOG_INFO, "Not using iptables since iptables uses nf_tables - please update configuration"); } FREE_CONST_PTR(global_data->vrrp_iptables_inchain); FREE_CONST_PTR(global_data->vrrp_iptables_outchain); #ifdef _HAVE_LIBIPSET_ if (global_data->using_ipsets) disable_ipsets(); #endif /* If nftables table name not set up, set it to default */ if (!global_data->vrrp_nf_table_name) global_data->vrrp_nf_table_name = STRDUP(DEFAULT_NFTABLES_TABLE); } #endif /* add/remove iptables/nftables drop rules */ void firewall_handle_accept_mode(vrrp_t *vrrp, int cmd, #ifndef _WITH_IPTABLES_ __attribute__((unused)) #endif bool force) { #if defined _WITH_IPTABLES_ && defined _WITH_NFTABLES_ if (!checked_iptables_nft) check_iptables_nft(); #endif #ifdef _WITH_IPTABLES_ if (global_data->vrrp_iptables_inchain) handle_iptables_accept_mode(vrrp, cmd, force); #endif #ifdef _WITH_NFTABLES_ if (global_data->vrrp_nf_table_name) { if (cmd == IPADDRESS_ADD) nft_add_addresses(vrrp); else nft_remove_addresses(vrrp); } #endif vrrp->firewall_rules_set = (cmd == IPADDRESS_ADD); } void firewall_remove_rule_to_iplist(list_head_t *l) { #ifdef _WITH_IPTABLES_ if (global_data->vrrp_iptables_inchain) handle_iptable_rule_to_iplist(l, NULL, IPADDRESS_DEL, false); #endif #ifdef _WITH_NFTABLES_ if (global_data->vrrp_nf_table_name) nft_remove_addresses_iplist(l); #endif } #ifdef _HAVE_VRRP_VMAC_ void firewall_add_vmac(const vrrp_t *vrrp, #ifndef _WITH_NFTABLES_ __attribute__((unused)) #endif const interface_t *old_ifp) { #if defined _WITH_IPTABLES_ && defined _WITH_NFTABLES_ if (!checked_iptables_nft) check_iptables_nft(); #endif #ifdef _WITH_IPTABLES_ if (global_data->vrrp_iptables_outchain) iptables_add_vmac(vrrp->ifp, vrrp->family, __test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags)); #endif #ifdef _WITH_NFTABLES_ if (global_data->vrrp_nf_table_name) nft_add_vmac(vrrp->ifp, vrrp->family, __test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags), old_ifp); #endif } void firewall_remove_vmac(const vrrp_t *vrrp) { #ifdef _WITH_IPTABLES_ if (global_data->vrrp_iptables_outchain) iptables_remove_vmac(vrrp->ifp, vrrp->family, __test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags)); #endif #ifdef _WITH_NFTABLES_ if (global_data->vrrp_nf_table_name) nft_remove_vmac(vrrp->ifp, vrrp->family, __test_bit(VRRP_FLAG_EVIP_OTHER_FAMILY, &vrrp->flags)); #endif } #endif void firewall_fini(void) { #ifdef _WITH_IPTABLES_ if (global_data->vrrp_iptables_inchain || global_data->vrrp_iptables_outchain) iptables_fini(); #endif #ifdef _WITH_NFTABLES_ if (global_data->vrrp_nf_table_name) nft_end(); #endif } keepalived-2.3.3/keepalived/include/0000775000175000017500000000000014772274312013140 5keepalived-2.3.3/keepalived/include/bfd_daemon.h0000664000175000017500000000260114011447617015302 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: bfd_daemon.c include file. * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #ifndef _BFD_DAEMON_H #define _BFD_DAEMON_H #define PROG_BFD "Keepalived_bfd" #ifdef _WITH_VRRP_ extern int bfd_vrrp_event_pipe[2]; #endif #ifdef _WITH_LVS_ extern int bfd_checker_event_pipe[2]; #endif extern bool open_bfd_pipes(void); extern int start_bfd_child(void); extern void bfd_validate_config(void); #ifdef THREAD_DUMP extern void register_bfd_parent_addresses(void); #endif #endif /* _BFD_DAEMON_H */ keepalived-2.3.3/keepalived/include/vrrp_ndisc.h0000664000175000017500000000356214705536371015412 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_ndisc.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_NDISC_H #define _VRRP_NDISC_H /* system includes */ #include #include #include #include /* local includes */ #include "vrrp.h" #include "vrrp_if.h" #include "vrrp_ipaddress.h" /* local definitions */ #define NDISC_HOPLIMIT 255 /* * IPv6 Header */ struct ip6hdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 priority:4, version:4; #elif defined(__BIG_ENDIAN_BITFIELD) __u8 version:4, priority:4; #else #error "Please fix " #endif __u8 flow_lbl[3]; __be16 payload_len; __u8 nexthdr; __u8 hop_limit; struct in6_addr saddr; struct in6_addr daddr; }; /* prototypes */ extern bool ndisc_init(void); extern void ndisc_close(void); extern void ndisc_send_unsolicited_na(ip_address_t *, unsigned); extern void ndisc_send_unsolicited_na_immediate(interface_t *, ip_address_t *); #endif keepalived-2.3.3/keepalived/include/bfd.h0000664000175000017500000001710714412066323013762 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: bfd.c include file. * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #ifndef _BFD_H #define _BFD_H #include #include #include #include "scheduler.h" #include "timer.h" #include "sockaddr.h" /* * RFC5881 */ #define BFD_CONTROL_PORT "3784" #define BFD_CONTROL_TTL 255 #define BFD_CONTROL_HOPLIMIT 64 #define BFD_MULTIHOP_CONTROL_PORT "4784" // See RFC5883 #define BFD_MIN_PORT 49152 #define BFD_MAX_PORT 65535 /* * Default parameters and limits */ #define BFD_MINRX_MIN 1U #define BFD_MINRX_MAX_SENSIBLE 10000U #define BFD_MINRX_MAX (UINT32_MAX / 1000U) #define BFD_MINRX_DEFAULT 10U #define BFD_MINTX_MIN 1U #define BFD_MINTX_MAX_SENSIBLE 10000U #define BFD_MINTX_MAX (UINT32_MAX / 1000U) #define BFD_MINTX_DEFAULT 10U #define BFD_IDLETX_MIN 1000U #define BFD_IDLETX_MAX_SENSIBLE 10000U #define BFD_IDLETX_MAX (UINT32_MAX / 1000U) #define BFD_IDLETX_DEFAULT 1000U #define BFD_MULTIPLIER_MIN 1U #define BFD_MULTIPLIER_MAX_SENSIBLE 10U #define BFD_MULTIPLIER_MAX 255U #define BFD_MULTIPLIER_DEFAULT 5U #define BFD_WEIGHT_MIN -253 #define BFD_WEIGHT_MAX 253 #define BFD_WEIGHT_DEFAULT 0 #define BFD_TTL_MAX 255 /* * BFD Session */ /* Maximum instance name length including \0 */ #define BFD_INAME_MAX 32 typedef struct _bfd { /* Configuration parameters */ char iname[BFD_INAME_MAX]; /* Instance name */ sockaddr_t nbr_addr; /* Neighbor address */ sockaddr_t src_addr; /* Source address */ uint32_t local_min_rx_intv; /* Required min RX interval */ uint32_t local_min_tx_intv; /* Desired min TX interval */ uint32_t local_idle_tx_intv; /* Desired idle TX interval */ uint8_t local_detect_mult; /* Local detection multiplier */ uint8_t ttl; /* TTL/hopcount to send */ uint8_t max_hops; /* Maximum number of hops allowed to be traversed by received packet */ bool passive; /* Operate in passive mode */ bool multihop; /* Set for multihop BFD (RFC5883) */ #ifdef _WITH_VRRP_ bool vrrp; /* Only send events to VRRP process */ #endif #ifdef _WITH_LVS_ bool checker; /* Only send events to checker process */ #endif /* Internal variables */ int fd_out; /* Output socket fd */ thread_ref_t thread_open_fd_out; /* Open out socket thread, used if cannot open after load/reload */ thread_ref_t thread_out; /* Output socket thread */ unsigned long sands_out; /* Output thread sands, used for suspend/resume */ thread_ref_t thread_exp; /* Expire thread */ unsigned long sands_exp; /* Expire thread sands, used for suspend/resume */ thread_ref_t thread_rst; /* Reset thread */ unsigned long sands_rst; /* Reset thread sands, used for suspend/resume */ bool send_error; /* Set if last send had an error */ /* State variables */ uint8_t local_state:2; /* Local state */ uint8_t remote_state:2; /* Remote state */ uint32_t local_discr; /* Local discriminator */ uint32_t remote_discr; /* Remote discriminator */ uint8_t local_diag:5; /* Local diagnostic code */ uint8_t remote_diag:5; /* Local diagnostic code */ uint32_t remote_min_tx_intv; /* Remote min TX interval */ uint32_t remote_min_rx_intv; /* Remote min RX interval */ uint8_t local_demand; /* Local demand mode */ uint8_t remote_demand; /* Remote demand mode */ uint8_t remote_detect_mult; /* Remote detection multiplier */ bool poll; /* Poll sequence flag */ bool final; /* Final flag */ /* Calculated values */ uint32_t local_tx_intv; /* Local transmit interval */ uint32_t remote_tx_intv; /* Remote transmit interval */ uint64_t local_detect_time; /* Local detection time */ uint64_t remote_detect_time; /* Remote detection time */ timeval_t last_seen; /* Time of the last packet received */ /* Linked list member */ list_head_t e_list; } bfd_t; /* * BFD Control Packet Header */ typedef struct _bfdhdr { #if __BYTE_ORDER == __LITTLE_ENDIAN uint8_t diag:5; uint8_t version:3; /* flags */ uint8_t multipoint:1; uint8_t demand:1; uint8_t auth:1; uint8_t cplane:1; uint8_t final:1; uint8_t poll:1; uint8_t state:2; #elif __BYTE_ORDER == __BIG_ENDIAN uint8_t version:3; uint8_t diag:5; /* flags */ uint8_t state:2; uint8_t poll:1; uint8_t final:1; uint8_t cplane:1; uint8_t auth:1; uint8_t demand:1; uint8_t multipoint:1; #else #error "Unsupported byte order" #endif uint8_t detect_mult; uint8_t len; uint32_t local_discr; uint32_t remote_discr; uint32_t min_tx_intv; uint32_t min_rx_intv; uint32_t min_echo_rx_intv; } bfdhdr_t; /* Version */ #define BFD_VERSION_1 1U /* State (Sta) */ #define BFD_STATE_ADMINDOWN 0U #define BFD_STATE_DOWN 1U #define BFD_STATE_INIT 2U #define BFD_STATE_UP 3U #define BFD_STATE_STR(s) \ (BFD_STATE_ADMINDOWN == s ? "AdminDown" : \ (BFD_STATE_DOWN == s ? "Down" : \ (BFD_STATE_INIT == s ? "Init" : \ (BFD_STATE_UP == s ? "Up" : "Unknown")))) #define BFD_ISADMINDOWN(b) (b->local_state == BFD_STATE_ADMINDOWN) #define BFD_ISDOWN(b) (b->local_state == BFD_STATE_DOWN) #define BFD_ISINIT(b) (b->local_state == BFD_STATE_INIT) #define BFD_ISUP(b) (b->local_state == BFD_STATE_UP) /* Diagnostic (Diag) */ #define BFD_DIAG_NO_DIAG 0U #define BFD_DIAG_EXPIRED 1U #define BFD_DIAG_ECHO_FAILED 2U #define BFD_DIAG_NBR_SIGNALLED_DOWN 3U #define BFD_DIAG_FWD_PLANE_RESET 4U #define BFD_DIAG_PATH_DOWN 5U #define BFD_DIAG_CAT_PATH_DOWN 6U #define BFD_DIAG_ADMIN_DOWN 7U #define BFD_DIAG_RCAT_PATH_DOWN 8U #define BFD_DIAG_STR(d) \ (BFD_DIAG_NO_DIAG == d ? "No Diagnostic" : \ (BFD_DIAG_EXPIRED == d ? "Control Detection Time Expired" : \ (BFD_DIAG_ECHO_FAILED == d ? "Echo Function Failed" : \ (BFD_DIAG_NBR_SIGNALLED_DOWN == d ? "Neighbor Signaled Session Down" : \ (BFD_DIAG_FWD_PLANE_RESET == d ? "Forwarding Plane Reset" : \ (BFD_DIAG_PATH_DOWN == d ? "Path Down" : \ (BFD_DIAG_CAT_PATH_DOWN == d ? "Concatenated Path Down" : \ (BFD_DIAG_ADMIN_DOWN == d ? "Administratively Down" : \ (BFD_DIAG_RCAT_PATH_DOWN == d ? "Reverse Concatenated Path Down" : "Unknown"))))))))) #define BFD_VALID_DIAG(d) (d <= 8) /* * BFD Packet structure */ typedef struct _bfdpkt { bfdhdr_t *hdr; sockaddr_t src_addr; sockaddr_t dst_addr; unsigned int ttl; unsigned int len; const char *buf; } bfdpkt_t; extern void bfd_update_local_tx_intv(bfd_t *); extern void bfd_update_remote_tx_intv(bfd_t *); extern void bfd_idle_local_tx_intv(bfd_t * bfd); extern void bfd_set_poll(bfd_t *); extern void bfd_reset_state(bfd_t *); extern void bfd_init_state(bfd_t *); extern void bfd_copy_state(bfd_t *, const bfd_t *, bool); extern void bfd_copy_sands(bfd_t *, const bfd_t *); extern bool bfd_check_packet(const bfdpkt_t *); extern bool bfd_check_packet_ttl(const bfdpkt_t *, const bfd_t *); extern void bfd_build_packet(bfdpkt_t * pkt, bfd_t *, char *, const ssize_t); #endif /* _BFD_H */ keepalived-2.3.3/keepalived/include/vrrp_sync.h0000664000175000017500000000277613664151677015301 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_sync.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_SYNC_H #define _VRRP_SYNC_H /* local include */ #include "vrrp.h" /* TSM size */ #define VRRP_MAX_TSM_STATE 3 /* MACRO definition */ #define GROUP_STATE(G) ((G)->state) #define GROUP_NAME(G) ((G)->gname) /* extern prototypes */ extern vrrp_t *vrrp_get_instance(char *) __attribute__ ((pure)); extern bool vrrp_sync_set_group(vrrp_sgroup_t *); extern bool vrrp_sync_can_goto_master(vrrp_t *); extern void vrrp_sync_backup(vrrp_t *); extern void vrrp_sync_master(vrrp_t *); extern void vrrp_sync_fault(vrrp_t *); #endif keepalived-2.3.3/keepalived/include/vrrp_if_config.h0000664000175000017500000000302614512523637016225 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_if_config.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_IF_CONFIG_H #define _VRRP_IF_CONFIG_H 1 #include "config.h" #include #include "vrrp_if.h" /* prototypes */ extern void set_promote_secondaries(interface_t*); extern void reset_promote_secondaries(interface_t*); #ifdef _HAVE_VRRP_VMAC_ extern void restore_rp_filter(void); extern void set_interface_parameters(const interface_t*, interface_t*, sa_family_t); extern void reset_interface_parameters(interface_t*); extern void link_set_ipv6(const interface_t*, bool); #endif extern void set_ipv6_forwarding(interface_t *); #endif keepalived-2.3.3/keepalived/include/check_udp.h0000664000175000017500000000265213734075113015156 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_udp.c include file. * * Author: Jie Liu, * * 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) 2019-2019 Alexandre Cassen, */ #ifndef _CHECK_UDP_H #define _CHECK_UDP_H #include "config.h" #include typedef struct _udp_check { uint16_t payload_len; uint8_t *payload; bool require_reply; uint16_t reply_len; uint8_t *reply_data; uint8_t *reply_mask; uint16_t min_reply_len; uint16_t max_reply_len; } udp_check_t; /* Prototypes defs */ extern void install_udp_check_keyword(void); #ifdef THREAD_DUMP extern void register_check_udp_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/main.h0000664000175000017500000000702414655677312014167 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Main program include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _MAIN_H #define _MAIN_H #include "config.h" /* global includes */ #include #include #include "scheduler.h" #include "pidfile.h" /* State flags */ enum daemon_bits { #ifdef _WITH_VRRP_ DAEMON_VRRP, #endif #ifdef _WITH_LVS_ DAEMON_CHECKERS, #endif #ifdef _WITH_BFD_ DAEMON_BFD, #endif RUN_ALL_CHILDREN, }; /* Reloading helpers */ #define SET_RELOAD (reload = 1) #define UNSET_RELOAD (reload = 0) /* Special pseudo-pointer to indicate default reload file */ #define DEFAULT_RELOAD_FILE ((void *)1) /* Global vars exported */ extern const char *version_string; /* keepalived version */ extern unsigned long daemon_mode; /* Which child processes are run */ extern const char *conf_file; /* Configuration file */ #ifdef _WITH_VRRP_ extern pid_t vrrp_child; /* VRRP child process ID */ extern pidfile_t vrrp_pidfile; /* overrule default pidfile */ extern bool have_vrrp_instances; /* vrrp instances configured */ #endif #ifdef _WITH_LVS_ extern pid_t checkers_child; /* Healthcheckers child process ID */ extern pidfile_t checkers_pidfile; /* overrule default pidfile */ extern bool have_virtual_servers; /* virtual servers configured */ #endif #ifdef _WITH_BFD_ extern pid_t bfd_child; /* BFD child process ID */ extern pidfile_t bfd_pidfile; /* overrule default pidfile */ extern bool have_bfd_instances; /* bfd instances configured */ #endif extern bool reload; /* Set during a reload */ extern pidfile_t main_pidfile; /* overrule default pidfile */ #ifdef _WITH_SNMP_ extern bool snmp_option; /* Enable SNMP support */ extern const char *snmp_socket; /* Socket to use for SNMP agent */ #endif extern bool use_pid_dir; /* pid files in /run/keepalived */ extern bool children_started; /* Set once children have been run first time */ extern unsigned os_major; /* Kernel version */ extern unsigned os_minor; extern unsigned os_release; extern bool ignore_sigint; extern void free_parent_mallocs_startup(bool); extern void free_parent_mallocs_exit(void); extern const char *make_syslog_ident(const char*); #ifdef _WITH_VRRP_ extern bool running_vrrp(void) __attribute__ ((pure)); #endif #ifdef _WITH_LVS_ extern bool running_checker(void) __attribute__ ((pure)); #endif extern void reinitialise_global_vars(void); extern void start_reload(thread_ref_t); #ifdef THREAD_DUMP extern void thread_dump_signal(void *, int); #endif extern void initialise_debug_options(void); extern int keepalived_main(int, char**); /* The "real" main function */ extern unsigned child_wait_time; extern bool umask_cmdline; extern unsigned num_reloading; #endif keepalived-2.3.3/keepalived/include/vrrp_arp.h0000664000175000017500000000424114705536371015067 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_arp.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_ARP_H #define _VRRP_ARP_H /* system includes */ #include #include #include /* local includes */ #include "vrrp.h" #include "vrrp_if.h" #include "vrrp_ipaddress.h" /* * Private link layer socket structure to hold infiniband size address * The infiniband MAC address is 20 bytes long */ struct sockaddr_large_ll { unsigned short sll_family; __be16 sll_protocol; int sll_ifindex; unsigned short sll_hatype; unsigned char sll_pkttype; unsigned char sll_halen; unsigned char sll_addr[INFINIBAND_ALEN]; }; typedef struct inf_arphdr { uint16_t ar_hrd; uint16_t ar_pro; uint8_t ar_hln; uint8_t ar_pln; uint16_t ar_op; /* Infiniband arp looks like this */ unsigned char __ar_sha[INFINIBAND_ALEN]; unsigned char __ar_sip[4]; unsigned char __ar_tha[INFINIBAND_ALEN]; unsigned char __ar_tip[4]; } inf_arphdr_t; typedef struct ipoib_hdr { u_int16_t proto; u_int16_t reserved; } ipoib_hdr_t; /* prototypes */ extern bool gratuitous_arp_init(void); extern void gratuitous_arp_close(void); extern void send_gratuitous_arp(ip_address_t *, unsigned); extern ssize_t send_gratuitous_arp_immediate(interface_t *, ip_address_t *); #endif keepalived-2.3.3/keepalived/include/global_data.h0000664000175000017500000002123514756615757015503 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Dynamic data structure definition. * * Author: Alexandre Cassen, * * 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, */ #ifndef _GLOBAL_DATA_H #define _GLOBAL_DATA_H #include "config.h" /* system includes */ #include #include #include #include #include #ifdef _HAVE_LIBIPSET_ #include #endif #ifdef _WITH_NFTABLES_ #include #endif #include /* local includes */ #include "list_head.h" #include "vrrp_if.h" #include "timer.h" #ifdef _WITH_VRRP_ #include "vrrp.h" #endif #ifdef _WITH_LVS_ #include "ipvswrapper.h" #include "libipvs.h" #endif #include "notify.h" #include "sockaddr.h" /* constants */ #define DEFAULT_SMTP_CONNECTION_TIMEOUT (30 * TIMER_HZ) #ifdef _WITH_VRRP_ #define RX_BUFS_POLICY_MTU 0x01 #define RX_BUFS_POLICY_ADVERT 0x02 #define RX_BUFS_SIZE 0x04 #endif #ifdef _WITH_LVS_ #define LVS_MAX_TIMEOUT (86400*31) /* 31 days */ #endif #ifdef _WITH_PROFILING_ extern void _start(void), etext(void); #endif /* email link list */ typedef struct _email { const char *addr; /* Linked list member */ list_head_t e_list; } email_t; #ifdef _WITH_LVS_ typedef enum { LVS_NO_FLUSH, LVS_FLUSH_FULL, LVS_FLUSH_VS } lvs_flush_t; #endif /* Configuration data root */ typedef struct _data { const char *process_name; #ifdef _WITH_VRRP_ const char *vrrp_process_name; #endif #ifdef _WITH_LVS_ const char *lvs_process_name; #endif #ifdef _WITH_BFD_ const char *bfd_process_name; #endif const char *network_namespace; /* network namespace name */ const char *network_namespace_ipvs; /* network namespace name for ipvs */ bool namespace_with_ipsets; /* override for namespaces with ipsets on Linux < 3.13 */ const char *local_name; const char *instance_name; /* keepalived instance name */ #ifdef _WITH_LINKBEAT_ bool linkbeat_use_polling; #endif const char *router_id; const char *email_from; sockaddr_t smtp_server; const char *smtp_helo_name; unsigned long smtp_connection_to; list_head_t email; int smtp_alert; notify_script_t *startup_script; unsigned startup_script_timeout; notify_script_t *shutdown_script; unsigned shutdown_script_timeout; bool use_symlinks; #ifndef _ONE_PROCESS_DEBUG_ const char *reload_check_config; /* log file name for validating new configuration before reloading */ const char *reload_time_file; bool reload_repeat; time_t reload_time; bool reload_date_specified; const char *reload_file; #endif const char *config_directory; bool data_use_instance; #ifdef _WITH_VRRP_ bool dynamic_interfaces; bool allow_if_changes; bool no_email_faults; int smtp_alert_vrrp; const char *default_ifname; /* Name of default interface */ interface_t *default_ifp; /* Default interface for static addresses */ bool disable_local_igmp; bool v3_checksum_as_v2; #endif #ifdef _WITH_LVS_ ipvs_timeout_t lvs_timeouts; int smtp_alert_checker; bool checker_log_all_failures; struct lvs_syncd_config lvs_syncd; bool lvs_flush; /* flush any residual LVS config at startup */ lvs_flush_t lvs_flush_on_stop; /* flush any LVS config at shutdown */ #endif int max_auto_priority; unsigned min_auto_priority_delay; #ifdef _WITH_VRRP_ struct sockaddr_in6 vrrp_mcast_group6 __attribute__((aligned(__alignof__(sockaddr_t)))); struct sockaddr_in vrrp_mcast_group4 __attribute__((aligned(__alignof__(sockaddr_t)))); unsigned vrrp_garp_delay; timeval_t vrrp_garp_refresh; unsigned vrrp_garp_rep; unsigned vrrp_garp_refresh_rep; unsigned vrrp_garp_lower_prio_delay; unsigned vrrp_garp_lower_prio_rep; unsigned vrrp_garp_interval; unsigned vrrp_gna_interval; unsigned vrrp_down_timer_adverts; #ifdef _HAVE_VRRP_VMAC_ unsigned vrrp_vmac_garp_intvl; bool vrrp_vmac_garp_all_if; #endif bool vrrp_lower_prio_no_advert; bool vrrp_higher_prio_send_advert; int vrrp_version; /* VRRP version (2 or 3) */ #ifdef _WITH_IPTABLES_ const char *vrrp_iptables_inchain; const char *vrrp_iptables_outchain; #ifdef _HAVE_LIBIPSET_ unsigned using_ipsets; const char *vrrp_ipset_address; const char *vrrp_ipset_address6; const char *vrrp_ipset_address_iface6; const char *vrrp_ipset_igmp; const char *vrrp_ipset_mld; #ifdef _HAVE_VRRP_VMAC_ const char *vrrp_ipset_vmac_nd; #endif #endif #endif #ifdef _WITH_NFTABLES_ const char *vrrp_nf_table_name; int vrrp_nf_chain_priority; bool vrrp_nf_ifindex; #endif bool vrrp_check_unicast_src; bool vrrp_skip_check_adv_addr; bool vrrp_strict; bool have_vrrp_config; char vrrp_process_priority; bool vrrp_no_swap; unsigned vrrp_realtime_priority; cpu_set_t vrrp_cpu_mask; rlim_t vrrp_rlimit_rt; #endif #ifdef _WITH_LVS_ bool have_checker_config; char checker_process_priority; bool checker_no_swap; unsigned checker_realtime_priority; cpu_set_t checker_cpu_mask; rlim_t checker_rlimit_rt; #ifdef _WITH_NFTABLES_ const char *ipvs_nf_table_name; int ipvs_nf_chain_priority; uint32_t ipvs_nftables_start_fwmark; #endif #endif #ifdef _WITH_NFTABLES_ bool nf_counters; #endif #ifdef _WITH_BFD_ bool have_bfd_config; char bfd_process_priority; bool bfd_no_swap; unsigned bfd_realtime_priority; cpu_set_t bfd_cpu_mask; rlim_t bfd_rlimit_rt; #endif notify_fifo_t notify_fifo; #ifdef _WITH_VRRP_ notify_fifo_t vrrp_notify_fifo; bool fifo_write_vrrp_states_on_reload; #endif #ifdef _WITH_LVS_ notify_fifo_t lvs_notify_fifo; #endif #ifdef _WITH_VRRP_ int vrrp_notify_priority_changes; #endif #ifdef _WITH_SNMP_ bool enable_traps; const char *snmp_socket; #ifdef _WITH_VRRP_ #ifdef _WITH_SNMP_VRRP_ bool enable_snmp_vrrp; #endif #ifdef _WITH_SNMP_RFCV2_ bool enable_snmp_rfcv2; #endif #ifdef _WITH_SNMP_RFCV3_ bool enable_snmp_rfcv3; #endif #endif #ifdef _WITH_SNMP_CHECKER_ bool enable_snmp_checker; unsigned long snmp_vs_stats_update_interval; unsigned long snmp_rs_stats_update_interval; #endif #endif #ifdef _WITH_DBUS_ bool enable_dbus; const char *dbus_service_name; const char *dbus_no_interface_name; #endif #ifdef _WITH_VRRP_ unsigned vrrp_netlink_cmd_rcv_bufs; bool vrrp_netlink_cmd_rcv_bufs_force; unsigned vrrp_netlink_monitor_rcv_bufs; bool vrrp_netlink_monitor_rcv_bufs_force; #ifdef _WITH_TRACK_PROCESS_ unsigned process_monitor_rcv_bufs; bool process_monitor_rcv_bufs_force; #endif #endif #ifdef _WITH_LVS_ unsigned lvs_netlink_cmd_rcv_bufs; bool lvs_netlink_cmd_rcv_bufs_force; unsigned lvs_netlink_monitor_rcv_bufs; bool lvs_netlink_monitor_rcv_bufs_force; #endif #ifdef _WITH_LVS_ bool rs_init_notifies; bool no_checker_emails; #endif #ifdef _WITH_VRRP_ int vrrp_rx_bufs_policy; size_t vrrp_rx_bufs_size; int vrrp_rx_bufs_multiples; unsigned vrrp_startup_delay; bool log_unknown_vrids; bool vrrp_owner_ignore_adverts; #ifdef _HAVE_VRRP_VMAC_ const char *vmac_prefix; const char *vmac_addr_prefix; #endif #endif #ifdef _WITH_JSON_ unsigned json_version; #endif #ifdef _WITH_VRRP_ const char *iproute_usr_dir; const char *iproute_etc_dir; #endif const char *state_dump_file; const char *stats_dump_file; const char *json_dump_file; } data_t; /* Global vars exported */ extern data_t *global_data; /* Global configuration data */ extern data_t *old_global_data; /* Old global configuration data - used during reload */ /* Prototypes */ extern const char * format_email_addr(const char *); extern void alloc_email(const char *); extern data_t *alloc_global_data(void); extern void init_global_data(data_t *, data_t *, bool); extern void free_global_data(data_t **); extern FILE *open_dump_file(const char *) __attribute__((malloc)); extern void dump_global_data(FILE *, data_t *); #endif keepalived-2.3.3/keepalived/include/vrrp_iproute.h0000664000175000017500000001565214631352176016001 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_iproute.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_IPROUTE_H #define _VRRP_IPROUTE_H /* global includes */ #include #include #include #include #if HAVE_DECL_RTA_ENCAP && HAVE_DECL_LWTUNNEL_ENCAP_MPLS #include #endif #include /* local includes */ #include "list_head.h" #include "vector.h" #include "vrrp_ipaddress.h" #include "vrrp_if.h" #include "vrrp_static_track.h" /* RTPROT_KEEPALIVED added in Linux 5.8 */ #ifndef RTPROT_KEEPALIVED #define RTPROT_KEEPALIVED 18 /* Keepalived daemon */ #endif /* Buffer sizes for printing */ #define ROUTE_BUF_SIZE 1024 /* types definition */ #if HAVE_DECL_RTA_ENCAP /* Since Linux 4.3 */ enum iproute_encap { IPROUTE_ENCAP_ID, IPROUTE_ENCAP_DSFIELD, IPROUTE_ENCAP_HOPLIMIT, IPROUTE_ENCAP_TTL = IPROUTE_ENCAP_HOPLIMIT, IPROUTE_ENCAP_FLAGS, }; #define IPROUTE_BIT_ENCAP_ID (1< * */ #ifndef _LIBIPVS_H #define _LIBIPVS_H #include "config.h" #include "ip_vs.h" /* * The default IPVS_SVC_PERSISTENT_TIMEOUT is a little larger than average * connection time plus IPVS TCP FIN timeout (2*60 seconds). Because the * connection template won't be released until its controlled connection * entries are expired. * If IPVS_SVC_PERSISTENT_TIMEOUT is too less, the template will expire * soon and will be put in expire again and again, which causes additional * overhead. If it is too large, the same will always visit the same * server, which may make dynamic load imbalance worse. */ #define IPVS_SVC_PERSISTENT_TIMEOUT (6*60) typedef struct ip_vs_service_app ipvs_service_t; typedef struct ip_vs_dest_app ipvs_dest_t; typedef struct ip_vs_timeout_user ipvs_timeout_t; typedef struct ip_vs_daemon_app ipvs_daemon_t; typedef struct ip_vs_service_entry_app ipvs_service_entry_t; typedef struct ip_vs_dest_entry_app ipvs_dest_entry_t; /* init socket and get ipvs info */ extern int ipvs_init(bool); /* Set timeout parameters */ extern int ipvs_set_timeout(const ipvs_timeout_t *to); /* flush all the rules */ extern int ipvs_flush(void); /* add a virtual service */ extern int ipvs_add_service(ipvs_service_t *svc); /* update a virtual service with new options */ extern int ipvs_update_service(ipvs_service_t *svc); /* delete a virtual service */ extern int ipvs_del_service(ipvs_service_t *svc); /* zero the counters of a service or all */ extern int ipvs_zero_service(ipvs_service_t *svc); /* add a destination server into a service */ extern int ipvs_add_dest(ipvs_service_t *svc, ipvs_dest_t *dest); /* update a destination server with new options */ extern int ipvs_update_dest(ipvs_service_t *svc, ipvs_dest_t *dest); /* remove a destination server from a service */ extern int ipvs_del_dest(ipvs_service_t *svc, ipvs_dest_t *dest); /* start a connection synchronizaiton daemon (master/backup) */ extern int ipvs_start_daemon(ipvs_daemon_t *dm); /* stop a connection synchronizaiton daemon (master/backup) */ extern int ipvs_stop_daemon(ipvs_daemon_t *dm); #ifdef _WITH_SNMP_CHECKER_ /* get the destination array of the specified service */ extern struct ip_vs_get_dests_app *ipvs_get_dests(__u32, __u16, __u16, union nf_inet_addr *, __u16, unsigned); /* get an ipvs service entry */ extern ipvs_service_entry_t * ipvs_get_service(__u32 fwmark, __u16 af, __u16 protocol, union nf_inet_addr *addr, __u16 port); #endif /* close the socket */ extern void ipvs_close(void); extern const char *ipvs_strerror(int err); #endif /* _LIBIPVS_H */ keepalived-2.3.3/keepalived/include/global_json.h0000664000175000017500000000251114401112313015476 /* * 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: Quentin Armitage * * 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) 2023 Quentin Armitage * Copyright (C) 2023-2023 Alexandre Cassen */ #ifndef _GLOBAL_JSON_H #define _GLOBAL_JSON_H /* https://jsonlint.com/ is useful to check validity of JSON output */ #define JSON_VERSION_V1 1 #define JSON_VERSION_V2 2 #endif keepalived-2.3.3/keepalived/include/namespaces.h0000664000175000017500000000310614011447617015344 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Linux namespace handling. * * Author: Quentin Armitage * * 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) 2016-2016 Alexandre Cassen, */ #ifndef _NAMESPACE_H #define _NAMESPACE_H #include #ifdef LIBIPVS_USE_NL #include #ifdef _LIBNL_DYNAMIC_ #include "libnl_link.h" #endif #ifdef _HAVE_LIBNL1_ #define nl_sock nl_handle #endif #endif extern void free_dirname(void); extern bool set_namespaces(const char*); extern void clear_namespaces(void); extern int set_netns_name(const char *); extern void restore_net_namespace(int); extern int socket_netns_name(const char *, int, int, int); /* ipvs namespaces */ #ifdef LIBIPVS_USE_NL extern int nl_ipvs_connect(const char *, struct nl_sock *); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_nftables.h0000664000175000017500000000305614134543436016102 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_nftables.c include file. * * Author: Quentin Armitage, * * 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-2020 Alexandre Cassen, */ #ifndef _VRRP_NFTABLES_H #define _VRRP_NFTABLES_H #include "config.h" #include #include "list_head.h" #include "nftables.h" #include "vrrp.h" #include "vrrp_ipaddress.h" #define DEFAULT_NFTABLES_TABLE "keepalived" extern void nft_add_addresses(vrrp_t *); extern void nft_remove_addresses(vrrp_t *); extern void nft_remove_addresses_iplist(list_head_t *); #ifdef _HAVE_VRRP_VMAC_ extern void nft_add_vmac(const interface_t *, int, bool, const interface_t *); extern void nft_remove_vmac(const interface_t *, int, bool); #endif extern void nft_end(void); #endif keepalived-2.3.3/keepalived/include/vrrp_json.h0000664000175000017500000000243614401112313015235 /* * 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: Damien Clabaut, * * 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) 2017 Damien Clabaut, * Copyright (C) 2017-2023 Alexandre Cassen, */ #ifndef _VRRP_JSON_H #define _VRRP_JSON_H #include "global_json.h" /* Prototypes */ extern void vrrp_print_json(void); #endif keepalived-2.3.3/keepalived/include/track_file.h0000664000175000017500000000711713743557751015352 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: track_file.c include file. * * Author: Alexandre Cassen, * * 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-2020 Alexandre Cassen, */ #ifndef _TRACK_FILE_H #define _TRACK_FILE_H /* global includes */ #include #include #include /* local includes */ #include "list_head.h" #ifdef _WITH_VRRP_ #include "vrrp.h" #endif #include "tracker.h" /* external file we read to track local processes */ typedef void (*obj_dump_func_t) (FILE *, const void *); typedef struct _tracked_file { const char *fname; /* File name */ const char *file_path; /* Path to file */ const char *file_part; /* Pointer to start of filename without directories */ int weight; /* Default weight */ bool weight_reverse; /* which direction is the weight applied */ int wd; /* Watch descriptor */ list_head_t tracking_obj; /* tracking_obj_t - for vrrp instances/real servers tracking this file */ obj_dump_func_t tracking_obj_dump; /* Dump helper for tracking_obj list */ int64_t last_status; /* Last status returned by file. Used to report changes */ bool reloaded; /* Set if this track_file existing in previous config */ /* linked list member */ list_head_t e_list; } tracked_file_t; /* Tracked file structure definition */ typedef struct _tracked_file_monitor { tracked_file_t *file; /* track file pointer, cannot be NULL */ int weight; /* Multiplier for file value */ bool weight_reverse; /* which direction is the weight applied */ /* linked list member */ list_head_t e_list; } tracked_file_monitor_t; static inline int weight_range(int64_t weight_long) { if (weight_long < INT_MIN) return INT_MIN; if (weight_long > INT_MAX) return INT_MAX; return weight_long; } extern void dump_track_file_monitor_list(FILE *, const list_head_t *); extern void free_track_file_monitor(tracked_file_monitor_t *); extern void free_track_file_monitor_list(list_head_t *); extern tracked_file_t * __attribute__ ((pure)) find_tracked_file_by_name(const char *, list_head_t *); extern void vrrp_alloc_track_file(const char *, list_head_t *, list_head_t *, const vector_t *); extern void add_track_file_keywords(bool active); extern void free_tracking_obj_list(list_head_t *); extern void dump_tracking_obj_list(FILE *fp, const list_head_t *, obj_dump_func_t); extern void free_track_file_list(list_head_t *); extern void dump_track_file_list(FILE *, const list_head_t *); extern void add_obj_to_track_file(void *, tracked_file_monitor_t *, const char *, obj_dump_func_t); extern void process_update_checker_track_file_status(const tracked_file_t *, int, const tracking_obj_t *); extern void init_track_files(list_head_t *); extern void stop_track_files(void); #ifdef THREAD_DUMP extern void register_track_file_inotify_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_ipsecah.h0000664000175000017500000000377513466412413015725 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_ipsecah.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_IPSEC_AH_H #define _VRRP_IPSEC_AH_H #include #include #include #include /* Predefined values */ #define HMAC_MD5_TRUNC 0x0C /* MD5 digest truncate value : 96-bit -- rfc2403.2 & rfc2104.5 */ #define IPSEC_AH_PLEN 0x04 /* Const for a 96-bit auth value : Computed in 32-bit words minus 2 => (HMAC_MD5_TRUNC*8+3*32)/32 - 2 -- rfc2402.2.2 */ typedef struct _ipsec_ah { /* rfc2402.2 */ uint8_t next_header; /* Next header field */ uint8_t payload_len; /* Payload Lenght */ uint16_t reserved; /* Reserved field */ uint32_t spi; /* Security Parameter Index */ uint32_t seq_number; /* Sequence number */ char auth_data[HMAC_MD5_TRUNC]; /* Authentication data 128-bit MD5 digest trucated */ } ipsec_ah_t; typedef struct _seq_counter { bool cycle; uint32_t seq_number; } seq_counter_t; extern void hmac_md5(const unsigned char *, size_t, const unsigned char *, size_t, const unsigned char *, size_t, unsigned char *); #endif keepalived-2.3.3/keepalived/include/smtp.h0000664000175000017500000000541714555732065014226 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: smtp.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _SMTP_H #define _SMTP_H /* globales includes */ #include #include /* local includes */ #include "global_data.h" #include "layer4.h" #ifdef _WITH_LVS_ #include "check_data.h" #include "check_api.h" #endif #ifdef _WITH_VRRP_ #include "vrrp.h" #endif /* global defs */ #define SMTP_PORT_STR "25" /* SMTP command stage. These values are used along with the enum connect_result * values in the SMTP FSM, and so need to follow them. */ enum smtp_cmd_state { HELO = connect_result_next, MAIL, RCPT, DATA, BODY, QUIT, END }; #define SMTP_MAX_FSM_STATE END /* SMTP mesage type format */ typedef enum { #ifdef _WITH_LVS_ SMTP_MSG_RS, SMTP_MSG_VS, SMTP_MSG_RS_SHUT, #endif #ifdef _WITH_VRRP_ SMTP_MSG_VGROUP, SMTP_MSG_VRRP, #endif } smtp_msg_t; /* SMTP thread argument structure */ #define MAX_HEADERS_LENGTH 256 #define MAX_BODY_LENGTH 512 /* SMTP FSM Macro */ #define SMTP_FSM_SEND(S, T) \ do { \ if ((*(SMTP_FSM[S].send))) \ (*(SMTP_FSM[S].send)) (T); \ } while (0) #define SMTP_FSM_READ(S, T) \ do { \ if ((*(SMTP_FSM[S].read))) \ (*(SMTP_FSM[S].read)) (T); \ } while (0) /* SMTP thread arguments */ typedef struct _smtp { int stage; email_t *next_email_element; char *subject; char *body; char *buffer; size_t buflen; } smtp_t; #define FMT_SMTP_HOST() inet_sockaddrtopair(&global_data->smtp_server) #ifdef _WITH_LVS_ typedef struct _smtp_rs { real_server_t *rs; virtual_server_t *vs; } smtp_rs; #else typedef void real_server_t; #endif #ifdef _SMTP_ALERT_DEBUG_ extern bool do_smtp_alert_debug; #endif #ifdef _SMTP_CONNECT_DEBUG_ extern bool do_smtp_connect_debug; #endif /* Prototypes defs */ extern void smtp_alert(smtp_msg_t, void *data, const char *, const char *); #ifdef THREAD_DUMP extern void register_smtp_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_track.h0000664000175000017500000002036314770002025015376 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_track.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_TRACK_H #define _VRRP_TRACK_H /* global includes */ #include #include #include /* local includes */ #include "vector.h" #include "list_head.h" #include "vrrp_if.h" #include "vrrp.h" #include "notify.h" #ifdef _WITH_BFD_ #include "bfd.h" #endif #ifdef _WITH_TRACK_PROCESS_ #include "rbtree_ka.h" #endif #include "tracker.h" /* VRRP script tracking defaults */ #define VRRP_SCRIPT_DI 1 /* external script track interval (in sec) */ #define VRRP_SCRIPT_DT 0 /* external script track timeout (in sec) */ #define VRRP_SCRIPT_DW 0 /* external script default weight */ /* VRRP script tracking results. * The result is an integer between 0 and rise-1 to indicate a DOWN state, * or between rise-1 and rise+fall-1 to indicate an UP state. Upon failure, * we decrease result and set it to zero when we pass below rise. Upon * success, we increase result and set it to rise+fall-1 when we pass above * rise-1. */ /* If a VRRP instance doesn't track it's own interface, we still * want the interface to have a reference to the VRRP instance, * but it needs to know the instance isn't tracking it. */ #define VRRP_NOT_TRACK_IF 255 /* external script we call to track local processes */ typedef struct _vrrp_script { const char *sname; /* instance name */ notify_script_t script; /* The script details */ unsigned long interval; /* interval between script calls */ unsigned long timeout; /* microseconds before script timeout */ int weight; /* weight associated to this script */ bool weight_reverse; /* which direction is the weight applied */ int result; /* result of last call to this script: 0..R-1 = KO, R..R+F-1 = OK */ int rise; /* R: how many successes before OK */ int fall; /* F: how many failures before KO */ list_head_t tracking_vrrp; /* tracking_obj_t - for vrrp instances tracking this script */ int last_status; /* Last status returned by script. Used to report changes */ script_state_t state; /* current state of script */ script_init_state_t init_state; /* current initialisation state of script */ bool insecure; /* Set if script is run by root, but is non-root modifiable */ /* linked list member */ list_head_t e_list; } vrrp_script_t; /* Tracked script structure definition */ typedef struct _tracked_sc { vrrp_script_t *scr; /* script pointer, cannot be NULL */ int weight; /* tracking weight when non-zero */ bool weight_reverse; /* which direction is the weight applied */ /* linked list member */ list_head_t e_list; } tracked_sc_t; #ifdef _WITH_TRACK_PROCESS_ typedef enum _param_match { PARAM_MATCH_NONE, PARAM_MATCH_EXACT, /* All parameters must match */ PARAM_MATCH_INITIAL, /* Match initial complete parameters */ PARAM_MATCH_PARTIAL, /* Allow the last parameter to be a partial match */ } param_match_t; /* process we track */ typedef struct _vrrp_tracked_process { const char *pname; /* Process name */ const char *process_path; /* Path to process */ const char *process_params; /* NUL separated parameters */ size_t process_params_len; /* Total length of parameters, including NULs */ param_match_t param_match; /* Full or partial match of parameters */ int weight; /* Default weight */ bool weight_reverse; /* which direction is the weight applied */ unsigned quorum; /* Minimum number of process instances required */ unsigned quorum_max; /* Maximum number of process instances required */ int fork_delay; /* Delay before processing process fork */ int terminate_delay; /* Delay before processing process termination */ bool full_command; /* Set if match against full command line */ thread_ref_t fork_timer_thread; /* For handling delay */ thread_ref_t terminate_timer_thread; /* For handling delay */ list_head_t tracking_vrrp; /* tracking_obj_t - for vrrp instances tracking this process */ unsigned num_cur_proc; bool have_quorum; /* Set if quorum is treated as achieved */ unsigned sav_num_cur_proc; /* Used if have ENOBUFS on netlink socket read */ /* linked list member */ list_head_t e_list; } vrrp_tracked_process_t; /* Tracked process structure definition */ typedef struct _tracked_process { vrrp_tracked_process_t *process; /* track process pointer, cannot be NULL */ int weight; /* Multiplier for process value */ bool weight_reverse; /* which direction is the weight applied */ /* linked list member */ list_head_t e_list; } tracked_process_t; /* A reference to tracked process */ typedef struct _ref_tracked_process { vrrp_tracked_process_t *process; /* track process pointer, cannot be NULL */ /* Linked list member */ list_head_t e_list; } ref_tracked_process_t; /* A monitored process instance */ typedef struct _tracked_process_instance { pid_t pid; list_head_t processes; /* ref_tracked_process_t */ /* rbtree member */ rb_node_t pid_tree; } tracked_process_instance_t; #endif #ifdef _WITH_BFD_ /* external bfd we read to track forwarding to remote systems */ typedef struct _vrrp_tracked_bfd { char bname[BFD_INAME_MAX]; /* bfd name */ int weight; /* Default weight */ bool weight_reverse; /* apply weight in opposite direction */ list_head_t tracking_vrrp; /* tracking_obj_t - for vrrp instances tracking this bfd */ bool bfd_up; /* Last status returned by bfd. Used to report changes */ /* linked list member */ list_head_t e_list; } vrrp_tracked_bfd_t; /* Tracked bfd structure definition */ typedef struct _tracked_bfd { vrrp_tracked_bfd_t *bfd; /* track bfd pointer, cannot be NULL */ int weight; /* Weight for bfd */ bool weight_reverse; /* which direction is the weight applied */ /* linked list member */ list_head_t e_list; } tracked_bfd_t; #endif /* Forward references */ struct _vrrp_t; struct _vrrp_sgroup; /* prototypes */ extern void dump_track_if_list(FILE *, const list_head_t *); extern void free_track_if(tracked_if_t *); extern void free_track_if_list(list_head_t *); extern void alloc_track_if(const char *, list_head_t *, const vector_t *); extern void dump_track_script_list(FILE *, const list_head_t *); extern void free_track_script(tracked_sc_t *); extern void free_track_script_list(list_head_t *); extern void alloc_track_script(const char *, list_head_t *, const vector_t *); #ifdef _WITH_TRACK_PROCESS_ extern void dump_track_process_list(FILE *, const list_head_t *); extern void free_track_process_list(list_head_t *); extern void alloc_track_process(const char *, list_head_t *, const vector_t *); #endif #ifdef _WITH_BFD_ extern vrrp_tracked_bfd_t *find_vrrp_tracked_bfd_by_name(const char *) __attribute__ ((pure)); extern vrrp_tracked_bfd_t *alloc_vrrp_tracked_bfd(const char *, list_head_t *); extern void dump_tracked_bfd_list(FILE *, const list_head_t *); extern void free_track_bfd(tracked_bfd_t *); extern void free_track_bfd_list(list_head_t *); extern void alloc_track_bfd(const char *, list_head_t *, const vector_t *); #endif extern vrrp_script_t *find_script_by_name(const char *) __attribute__ ((pure)); extern void update_script_priorities(vrrp_script_t *, bool); extern void down_instance(struct _vrrp_t *, unsigned); // last param should be vrrp_fault_fl_t); extern void vrrp_set_effective_priority(struct _vrrp_t *); extern void initialise_tracking_priorities(void); #ifdef _WITH_TRACK_PROCESS_ extern void process_update_track_process_status(vrrp_tracked_process_t *, bool); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_ipset.h0000664000175000017500000000346014460043445015424 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_ipset.c include file. * * Author: Quentin Armitage, * * 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, */ #ifndef _VRRP_IPSET_H #define _VRRP_IPSET_H #define LIBIPSET_NFPROTO_H #include "vrrp_ipaddress.h" #include "vrrp_iptables.h" #define DEFAULT_IPSET_NAME "keepalived" struct ipset_session; extern bool add_vip_ipsets(struct ipset_session **, uint8_t, bool); extern bool add_igmp_ipsets(struct ipset_session **, uint8_t, bool); extern bool remove_vip_ipsets(struct ipset_session **, uint8_t); extern bool remove_igmp_ipsets(struct ipset_session **, uint8_t); extern bool ipset_initialise(void); extern void* ipset_session_start(void); extern void ipset_session_end(void *); extern void ipset_entry(void *, int, const ip_address_t*); extern void ipset_entry_igmp(void*, int, const char *, uint8_t); extern void ipset_entry_nd(void*, int, const interface_t *); extern void set_default_ipsets(void); extern void disable_ipsets(void); #endif keepalived-2.3.3/keepalived/include/daemon.h0000664000175000017500000000207413717517530014477 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Daemon process handling. * * Author: Alexandre Cassen, * * 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, */ #ifndef _DAEMON_H #define _DAEMON_H /* prototype */ extern pid_t xdaemon(void); #endif keepalived-2.3.3/keepalived/include/check_api.h0000664000175000017500000001063314661620303015132 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Checkers arguments structures definitions. * * Author: Alexandre Cassen, * * 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, */ #ifndef _CHECK_API_H #define _CHECK_API_H #include "config.h" /* global includes */ #include #include #include /* local includes */ #include "list_head.h" #include "check_data.h" #include "vector.h" #include "layer4.h" #include "sockaddr.h" typedef enum _checker_type { CHECKER_MISC, CHECKER_TCP, CHECKER_UDP, CHECKER_DNS, CHECKER_HTTP, CHECKER_SSL, CHECKER_SMTP, CHECKER_BFD, CHECKER_PING, CHECKER_FILE } checker_type_t; /* Forward reference */ struct _checker; typedef struct _checker_funcs { checker_type_t type; void (*free_func) (struct _checker *); void (*dump_func) (FILE *, const struct _checker *); bool (*compare) (const struct _checker *, struct _checker *); void (*migrate) (struct _checker *, const struct _checker *); } checker_funcs_t; /* Checkers structure definition */ typedef struct _checker { const checker_funcs_t *checker_funcs; thread_func_t launch; virtual_server_t *vs; /* pointer to the checker thread virtualserver */ real_server_t *rs; /* pointer to the checker thread realserver */ void *data; /* Details for the specific checker type */ bool enabled; /* Activation flag */ bool is_up; /* Set if checker is up */ bool has_run; /* Set if the checker has completed at least once */ int cur_weight; /* Current weight of checker */ conn_opts_t *co; /* connection options */ int alpha; /* Alpha mode enabled */ unsigned long delay_loop; /* Interval between running checker */ unsigned long warmup; /* max random timeout to start checker */ unsigned retry; /* number of retries before failing */ unsigned long delay_before_retry; /* interval between retries */ unsigned retry_it; /* number of successive failures */ unsigned default_retry; /* number of retries before failing */ unsigned long default_delay_before_retry; /* interval between retries */ bool log_all_failures; /* Log all failures when checker up */ /* Linked list of checkers from rs */ list_head_t rs_list; } checker_t; typedef struct _checker_ref { checker_t *checker; /* Linked list member */ list_head_t e_list; } checker_ref_t; extern checker_t *current_checker; /* utility macro */ #define CHECKER_ARG(X) ((X)->data) #define CHECKER_NEW_CO() ((conn_opts_t *) MALLOC(sizeof (conn_opts_t))) #define FMT_CHK(C) FMT_RS((C)->rs, (C)->vs) #ifdef _WITH_NFTABLES_ #define VSG_USES_AUTO_FWMARK(vsg) (global_data->ipvs_nf_table_name && list_empty(&vsg->vfwmark)) #define VS_USES_VSG_AUTO_FWMARK(vs) (vs->vsg && VSG_USES_AUTO_FWMARK(vs->vsg)) #endif #ifdef _CHECKER_DEBUG_ extern bool do_checker_debug; #endif /* Prototypes definition */ extern void free_checker(checker_t *); extern void free_checker_list(list_head_t *); extern void free_rs_checkers(const real_server_t *); extern void dump_connection_opts(FILE *, const void *); extern void queue_checker(const checker_funcs_t * , thread_func_t , void * , conn_opts_t * , bool); extern void dequeue_new_checker(void); extern bool check_conn_opts(conn_opts_t *); extern bool compare_conn_opts(const conn_opts_t *, const conn_opts_t *) __attribute__ ((pure)); extern void dump_checkers(FILE *); extern void register_checkers_thread(void); extern void install_checkers_keyword(void); extern void checker_set_dst_port(sockaddr_t *, uint16_t); extern void install_checker_common_keywords(bool); extern void update_checker_activity(sa_family_t, void *, bool); #endif keepalived-2.3.3/keepalived/include/vrrp_iprule.h0000664000175000017500000000707514011453335015601 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_iprule.c include file. * * Author: Chris Riley, * * 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) 2015 Chris Riley, * Copyright (C) 2016-2017 Alexandre Cassen, */ #ifndef _VRRP_IPRULE_H #define _VRRP_IPRULE_H /* global includes */ #include #include #include #include #if HAVE_DECL_FRA_UID_RANGE #include #endif /* local includes */ #include "vrrp_if.h" #include "vrrp_ipaddress.h" #include "vrrp_static_track.h" /* print buffer sizes */ #define RULE_BUF_SIZE 256 enum iprule_param_mask { IPRULE_BIT_PRIORITY = 0x01, IPRULE_BIT_FWMARK = 0x02, IPRULE_BIT_FWMASK = 0x04, IPRULE_BIT_SUP_GROUP = 0x08, IPRULE_BIT_UID_RANGE = 0x10, #if HAVE_DECL_FRA_PROTOCOL IPRULE_BIT_PROTOCOL = 0x20, #endif #if HAVE_DECL_FRA_IP_PROTO IPRULE_BIT_IP_PROTO = 0x40, #endif #if HAVE_DECL_FRA_SPORT_RANGE IPRULE_BIT_SPORT_RANGE = 0x80, #endif #if HAVE_DECL_FRA_DPORT_RANGE IPRULE_BIT_DPORT_RANGE = 0x100, #endif } ; /* types definition */ typedef struct _ip_rule { uint32_t mask; bool invert; int family; ip_address_t *from_addr; ip_address_t *to_addr; uint32_t priority; uint8_t tos; uint32_t fwmark; uint32_t fwmask; uint32_t realms; #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN int32_t suppress_prefix_len; #endif #if HAVE_DECL_FRA_SUPPRESS_IFGROUP uint32_t suppress_group; #endif interface_t *iif; interface_t *oif; uint32_t goto_target; uint32_t table; uint8_t action; #if HAVE_DECL_FRA_TUN_ID uint64_t tunnel_id; #endif #if HAVE_DECL_FRA_UID_RANGE struct fib_rule_uid_range uid_range; #endif #if HAVE_DECL_FRA_L3MDEV bool l3mdev; #endif #if HAVE_DECL_FRA_PROTOCOL uint8_t protocol; #endif #if HAVE_DECL_FRA_IP_PROTO uint8_t ip_proto; #endif #if HAVE_DECL_FRA_SPORT_RANGE struct fib_rule_port_range src_port; #endif #if HAVE_DECL_FRA_DPORT_RANGE struct fib_rule_port_range dst_port; #endif bool dont_track; /* used for virtual rules */ static_track_group_t *track_group; /* used for static rules */ bool set; /* linked list member */ list_head_t e_list; } ip_rule_t; #define IPRULE_DEL 0 #define IPRULE_ADD 1 /* prototypes */ extern void reinstate_static_rule(ip_rule_t *); extern void netlink_rulelist(list_head_t *, int, bool); extern void free_iprule(ip_rule_t *); extern void free_iprule_list(list_head_t *); extern void format_iprule(const ip_rule_t *, char *, size_t); extern void dump_iprule(FILE *, const ip_rule_t *); extern void dump_iprule_list(FILE *, const list_head_t *); extern void alloc_rule(list_head_t *, const vector_t *, bool); extern void clear_diff_rules(list_head_t *, list_head_t *); extern void clear_diff_static_rules(void); extern void reset_next_rule_priority(void); #endif keepalived-2.3.3/keepalived/include/vrrp_vmac.h0000664000175000017500000000413214134543436015226 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_vmac.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_VMAC_H #define _VRRP_VMAC_H /* global includes */ #include #if defined _HAVE_NETINET_LINUX_IF_ETHER_H_COLLISION_ && \ defined _LINUX_IF_ETHER_H && \ !defined _NETINET_IF_ETHER_H #define _NETINET_IF_ETHER_H #endif #include #include /* local includes */ #include "vrrp.h" #include "vrrp_if.h" extern const char * const macvlan_ll_kind; extern const u_char ll_addr[ETH_ALEN]; /* prototypes */ extern bool add_link_local_address(interface_t *, struct in6_addr*); extern bool del_link_local_address(interface_t *); extern bool replace_link_local_address(interface_t *); extern bool reset_link_local_address(struct in6_addr*, vrrp_t *); #if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE extern void remove_vmac_auto_gen_addr(interface_t *, struct in6_addr *); #endif extern bool set_link_local_address(const vrrp_t *); extern bool netlink_link_add_vmac(vrrp_t *, const interface_t *); extern void netlink_link_del_vmac(vrrp_t *); #ifdef _HAVE_VRRP_IPVLAN_ extern bool netlink_link_add_ipvlan(vrrp_t *); #endif #ifdef _HAVE_VRF_ extern void update_vmac_vrfs(interface_t *); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_parser.h0000664000175000017500000000246014227044032015565 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_parser.c include file. * * Author: Alexandre Cassen, * * 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-2016 Alexandre Cassen, */ #ifndef _VRRP_PARSER_H #define _VRRP_PARSER_H /* Global include */ #include /* local include */ #include "vector.h" #include "vrrp.h" extern vrrp_t *current_vrrp; extern vrrp_sgroup_t *current_vsyncg; /* Prototypes */ extern const vector_t *vrrp_init_keywords(void); extern void init_vrrp_keywords(bool); #endif keepalived-2.3.3/keepalived/include/vrrp_print.h0000664000175000017500000000243513643201157015433 /* * 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: vrrp_print.c program include file. * * Author: John Southworth, * * 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) 2012 John Southworth, * Copyright (C) 2015-2017 Alexandre Cassen, */ #ifndef _VRRP_PRINT_H #define _VRRP_PRINT_H #include extern void vrrp_print_data(void); extern void vrrp_print_stats(bool); #endif keepalived-2.3.3/keepalived/include/vrrp_ipaddress.h0000664000175000017500000000767014707732520016270 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_ipaddress.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_IPADDR_H #define _VRRP_IPADDR_H #include "config.h" /* global includes */ #include #include #include #include /* local includes */ #include "vrrp.h" #include "vrrp_if.h" #include "list_head.h" #include "vector.h" #include "vrrp_static_track.h" #include "utils.h" /* types definition */ typedef struct _ip_address { struct ifaddrmsg ifa; union { struct { struct in_addr sin_addr; struct in_addr sin_brd; } sin; struct in6_addr sin6_addr; } u; interface_t *ifp; /* Interface owning IP address */ char *label; /* Alias name, e.g. eth0:1 */ #if HAVE_DECL_IFA_FLAGS uint32_t flags; /* Address flags */ uint32_t flagmask; /* Bitmaps of flags set */ #else uint8_t flags; /* Address flags */ uint8_t flagmask; /* Bitmaps of flags set */ #endif bool have_peer; bool use_vmac; union { struct in_addr sin_addr; struct in6_addr sin6_addr; } peer; bool dont_track; /* Don't leave master state if address is deleted */ static_track_group_t *track_group; /* used for static addresses */ bool set; /* TRUE if addr is set */ #ifdef _WITH_IPTABLES_ bool iptable_rule_set; /* TRUE if iptable drop rule * set to addr */ #endif #ifdef _WITH_NFTABLES_ bool nftable_rule_set; /* TRUE if in nftables set */ #endif unsigned garp_gna_pending; /* Number of GARPs/GNAs still to be sent */ list_head_t garp_gna_list; uint32_t preferred_lft; /* IPv6 preferred_lft (0 means address deprecated) */ /* linked list member */ list_head_t e_list; } ip_address_t; #define IPADDRESS_DEL 0 #define IPADDRESS_ADD 1 #define DFLT_INT "eth0" /* Macro definition */ #define IP_FAMILY(X) (X)->ifa.ifa_family #define IP_IS6(X) ((X)->ifa.ifa_family == AF_INET6) #define CLEAR_IP6_ADDR(X) ((X)->s6_addr32[0] = (X)->s6_addr32[1] = (X)->s6_addr32[2] = (X)->s6_addr32[3] = 0) #define IPADDRESSTOS_BUF_LEN (INET6_ADDRSTRLEN + 4) /* allow for subnet */ /* Forward reference */ struct ipt_handle; /* prototypes */ extern const char *ipaddresstos(char *, const ip_address_t *); extern bool compare_ipaddress(const ip_address_t *, const ip_address_t *) __attribute__((pure)); extern int netlink_ipaddress(ip_address_t *, int); extern bool netlink_iplist(list_head_t *, int, bool); extern void free_ipaddress(ip_address_t *); extern void free_ipaddress_list(list_head_t *); extern void format_ipaddress(const ip_address_t *, char *, size_t); extern void dump_ipaddress(FILE *, const ip_address_t *); extern void dump_ipaddress_list(FILE *, const list_head_t *); extern ip_address_t *parse_ipaddress(ip_address_t *, const char *, bool); extern ip_address_t *parse_route(const char *); extern ip_address_t *alloc_ipaddress(const vector_t *, bool); 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 keepalived-2.3.3/keepalived/include/vrrp_data.h0000664000175000017500000001021014706175715015211 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Dynamic data structure definition. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_DATA_H #define _VRRP_DATA_H /* system includes */ #include #include #include /* local includes */ #include "list_head.h" #include "vector.h" #include "vrrp_static_track.h" /* Configuration data root */ typedef struct _vrrp_data { list_head_t static_track_groups; /* static_track_group_t */ list_head_t static_addresses; /* ip_address_t */ list_head_t static_routes; /* ip_route_t */ list_head_t static_rules; /* ip_rule_t */ list_head_t vrrp_sync_group; /* vrrp_sgroup_t */ list_head_t vrrp; /* vrrp_t */ list_head_t vrrp_socket_pool; /* sock_t */ list_head_t vrrp_script; /* vrrp_script_t */ list_head_t vrrp_track_files; /* tracked_file_t */ #ifdef _WITH_TRACK_PROCESS_ list_head_t vrrp_track_processes; /* vrrp_tracked_process_t */ size_t vrrp_max_process_name_len; bool vrrp_use_process_cmdline; bool vrrp_use_process_comm; #endif #ifdef _WITH_BFD_ list_head_t vrrp_track_bfds; /* vrrp_tracked_bfd_t */ #endif unsigned num_smtp_alert; /* No of smtp_alerts configured */ } vrrp_data_t; /* Global Vars exported */ extern vrrp_data_t *vrrp_data; extern vrrp_data_t *old_vrrp_data; extern void *vrrp_buffer; extern size_t vrrp_buffer_len; /* prototypes */ extern static_track_group_t *alloc_static_track_group(const char *); extern void alloc_saddress(const vector_t *); extern void alloc_sroute(const vector_t *); extern void alloc_srule(const vector_t *); extern vrrp_sgroup_t *alloc_vrrp_sync_group(const char *); extern vrrp_t *alloc_vrrp(const char *); extern void alloc_vrrp_unicast_peer(const vector_t *); extern void alloc_vrrp_track_if(const vector_t *); extern vrrp_script_t *alloc_vrrp_script(const char *); extern void free_vscript(vrrp_script_t *); extern void alloc_vrrp_track_script(const vector_t *); extern void alloc_vrrp_track_file(const vector_t *); #ifdef _WITH_TRACK_PROCESS_ extern vrrp_tracked_process_t *alloc_vrrp_process(const char *); extern void free_vprocess(vrrp_tracked_process_t *); extern void alloc_vrrp_track_process(const vector_t *); #endif #ifdef _WITH_BFD_ extern void free_vrrp_tracked_bfd(vrrp_tracked_bfd_t *); extern void alloc_vrrp_track_bfd(const vector_t *); #endif extern void alloc_vrrp_group_track_if(const vector_t *); extern void alloc_vrrp_group_track_script(const vector_t *); extern void alloc_vrrp_group_track_file(const vector_t *); #ifdef _WITH_TRACK_PROCESS_ extern void alloc_vrrp_group_track_process(const vector_t *); #endif #ifdef _WITH_BFD_ extern void alloc_vrrp_group_track_bfd(const vector_t *); #endif extern void alloc_vrrp_vip(const vector_t *); extern void alloc_vrrp_evip(const vector_t *); extern void alloc_vrrp_vroute(const vector_t *); extern void alloc_vrrp_vrule(const vector_t *); extern void alloc_vrrp_buffer(size_t); extern void free_vrrp_buffer(void); extern vrrp_data_t *alloc_vrrp_data(void); extern void free_vrrp_data(vrrp_data_t **); extern void free_sync_group(vrrp_sgroup_t *); extern void free_sock_list(list_head_t *); extern void dump_sock_list(FILE *, const list_head_t *); extern void dump_tracking_vrrp(FILE *, const void *); extern void dump_tracking_vrrp_list(FILE *, const list_head_t *); extern void dump_data_vrrp(FILE *); #endif keepalived-2.3.3/keepalived/include/check_genhash.h0000664000175000017500000000276214070603273016003 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_genhash.c include file. * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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, */ #ifndef _CHECK_GENHASH_H #define _CHECK_GENHASH_H #include /* options bits */ enum genhash_option_bits { GENHASH_SERVER_BIT, GENHASH_PORT_BIT, GENHASH_URL_BIT, GENHASH_SSL_BIT, GENHASH_SNI_BIT, GENHASH_HASH_METHOD_BIT, GENHASH_VHOST_BIT, GENHASH_FWMARK_BIT, GENHASH_PROTO_BIT, GENHASH_TIMEOUT_BIT, }; /* Define prototypes */ extern void check_genhash(bool, int, char **); #endif keepalived-2.3.3/keepalived/include/vrrp_firewall.h0000664000175000017500000000334614134543436016113 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_firewall.c include file. * * Author: Alexandre Cassen, * * 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-2018 Alexandre Cassen, */ #ifndef _VRRP_FIREWALL_H #define _VRRP_FIREWALL_H #include "config.h" /* global includes */ #include /* local includes */ #include "vrrp.h" #include "vrrp_if.h" #ifdef _WITH_IPTABLES_ #include "vrrp_iptables.h" #endif #ifdef _WITH_NFTABLES_ #include "vrrp_nftables.h" #endif /* The following is defined in linux/icmpv6.h, but both it and * netinet/icmp6.h define struct icmp6_filter */ #define ICMPV6_MLD2_REPORT 143 /* prototypes */ extern void firewall_handle_accept_mode(vrrp_t *, int, bool); extern void firewall_remove_rule_to_iplist(list_head_t *); #ifdef _HAVE_VRRP_VMAC_ extern void firewall_add_vmac(const vrrp_t *, const interface_t *); extern void firewall_remove_vmac(const vrrp_t *); #endif extern void firewall_fini(void); #endif keepalived-2.3.3/keepalived/include/vrrp.h0000664000175000017500000005123214770002126014213 /* * 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: vrrp.c program include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_H #define _VRRP_H #include "config.h" /* system include */ #include #include #include #include #include #include #include /* local include */ #include "vector.h" #include "timer.h" #include "notify.h" #include "tracker.h" #if defined _WITH_VRRP_AUTH_ #include "vrrp_ipsecah.h" #endif #include "vrrp_if.h" #include "vrrp_sock.h" #include "vrrp_track.h" #include "sockaddr.h" struct _ip_address; enum vrrp_flags_bits { VRRP_FLAG_UNICAST_CONFIGURED, VRRP_FLAG_UNICAST, VRRP_FLAG_UNICAST_DUPLICATE_VRID, VRRP_FLAG_UNICAST_FAULT_NO_PEERS, VRRP_FLAG_DONT_TRACK_PRIMARY, /* If set ignores ifp faults */ VRRP_FLAG_LINKBEAT_USE_POLLING, /* Don't use netlink for interface status */ VRRP_FLAG_SKIP_CHECK_ADV_ADDR, /* If set, don't check the VIPs in subsequent adverts from the same master */ VRRP_FLAG_SADDR_FROM_CONFIG, /* Set if the source address is from configuration */ VRRP_FLAG_TRACK_SADDR, /* Fault state if configured saddr is missing */ VRRP_FLAG_CHECK_UNICAST_SRC, /* It set, check the source address of a unicast advert */ VRRP_FLAG_PROMOTE_SECONDARIES, /* Set promote_secondaries option on interface */ VRRP_FLAG_EVIP_OTHER_FAMILY, /* There are eVIPs of the different address family from the vrrp family */ VRRP_FLAG_ALLOW_NO_VIPS, /* Suppresses warnings re no VIPs */ VRRP_FLAG_NOPREEMPT, /* true if higher prio does not preempt lower */ VRRP_FLAG_V3_CHECKSUM_AS_V2, /* Omit pseudo header from VRRPv3 IPv4 checksum calculation */ #ifdef _HAVE_VRRP_VMAC_ VRRP_VMAC_BIT, VRRP_VMAC_UP_BIT, VRRP_VMAC_XMITBASE_BIT, VRRP_VMAC_ADDR_BIT, VRRP_VMAC_NETLINK_NOTIFY, VRRP_VMAC_GROUP, #ifdef _HAVE_VRRP_IPVLAN_ VRRP_IPVLAN_BIT, #endif VRRP_VMAC_MAC_SPECIFIED, VRRP_VMAC_MAC_USE_VRID, VRRP_FLAG_DUPLICATE_VRID_FAULT, /* Set if we have a fault due to duplicate VRID */ VRRP_FLAG_VMAC_GARP_ALL_IF, /* Send GARPs on all i/fs, not just VMACs */ #endif }; typedef enum vrrp_rlflags { VRRP_RLFLAG_INVALID_TTL = 0x1, VRRP_RLFLAG_WRONG_VERSION = 0x2, VRRP_RLFLAG_NOT_ADVERTISEMENT = 0x4, VRRP_RLFLAG_INCOMPLETE_PACKET = 0x8, VRRP_RLFLAG_NO_VIPS = 0x10, VRRP_RLFLAG_WRONG_ADDR_COUNT = 0x20, VRRP_RLFLAG_VIPS_MISMATCH = 0x40, VRRP_RLFLAG_WRONG_AUTH = 0x80, VRRP_RLFLAG_BAD_AUTH = 0x100, VRRP_RLFLAG_BAD_AH_HEADER = 0x200, VRRP_RLFLAG_BAD_IP_VERSION = 0x400, VRRP_RLFLAG_BAD_LENGTH = 0x800, VRRP_RLFLAG_WRONG_AUTH_PASSWD = 0x1000, VRRP_RLFLAG_ADV_INTVL_MISMATCH = 0x2000, VRRP_RLFLAG_BAD_CHECKSUM = 0x4000, VRRP_RLFLAG_UNI_MULTICAST_ERR = 0x8000, VRRP_RLFLAG_UNKNOWN_UNICAST_SRC = 0x10000, VRRP_RLFLAG_TTL_NOT_IN_RANGE = 0x20000, VRRP_RLFLAG_OWNER_IGNORE_ADVER = 0x40000, } vrrp_rlflags_t; typedef struct _vrrphdr { /* rfc2338.5.1 */ uint8_t vers_type; /* 0-3=type, 4-7=version */ uint8_t vrid; /* virtual router id */ uint8_t priority; /* router priority */ uint8_t naddr; /* address counter */ union { struct { uint8_t auth_type; /* authentification type */ uint8_t adver_int; /* advertisement interval (in sec) */ } v2; struct { uint16_t adver_int; /* advertisement interval (in centi-sec (10ms)) */ } v3; }; uint16_t chksum; /* checksum (ip-like one) */ /* here ip addresses */ /* here authentification infos */ } vrrphdr_t; typedef struct { uint32_t src; uint32_t dst; uint8_t zero; uint8_t proto; uint16_t len; } ipv4_phdr_t; /* protocol constants */ #define INADDR_VRRP_GROUP "224.0.0.18" /* multicast IPv4 addr - rfc2338.5.2.2 */ #define INADDR6_VRRP_GROUP "ff02::12" /* multicast IPv6 addr - rfc5798.5.1.2.2 */ #define VRRP_IP_TTL 255 /* in and out pkt ttl -- rfc2338.5.2.3 */ #define IPPROTO_VRRP 112 /* IP protocol number -- rfc2338.5.2.4 */ #define VRRP_VERSION_2 2 /* VRRP version 2 -- rfc2338.5.3.1 */ #define VRRP_VERSION_3 3 /* VRRP version 3 -- rfc5798.5.2.1 */ #define VRRP_PKT_ADVERT 1 /* packet type -- rfc2338.5.3.2 */ #define VRRP_PRIO_OWNER 255 /* priority of the ip owner -- rfc2338.5.3.4 */ #define VRRP_PRIO_DFL 100 /* default priority -- rfc2338.5.3.4 */ #define VRRP_PRIO_STOP 0 /* priority to stop -- rfc2338.5.3.4 */ #define VRRP_MAX_ADDR 0xFF /* count addr field is 8 bits wide */ #define VRRP_AUTH_NONE 0 /* no authentification -- rfc2338.5.3.6 */ #ifdef _WITH_VRRP_AUTH_ #define VRRP_AUTH_PASS 1 /* password authentification -- rfc2338.5.3.6 */ #define VRRP_AUTH_AH 2 /* AH(IPSec) authentification - rfc2338.5.3.6 */ #endif #define VRRP_ADVER_DFL 1 /* advert. interval (in sec) -- rfc2338.5.3.7 */ #define VRRP_DOWN_TIMER_ADVERTS 3 /* number of adverts to miss before master down -- rfc5798.6.1 & rfc3768.6.1 */ #define VRRP_GARP_DELAY (5 * TIMER_HZ) /* Default delay to launch gratuitous arp */ #define VRRP_GARP_REP 5 /* Default repeat value for MASTER state gratuitous arp */ #define VRRP_GARP_REFRESH 0 /* Default interval for refresh gratuitous arp (0 = none) */ #define VRRP_GARP_REFRESH_REP 1 /* Default repeat value for refresh gratuitous arp */ #define V3_PKT_ADVER_INT_NTOH(ai) (ntohs(ai) & 0xFFF) #define V3_PKT_ADVER_INT_HTON(ai) (htons(ai & 0xFFF)) /* * parameters per vrrp sync group. A vrrp_sync_group is a set * of VRRP instances that need to be state sync together. */ typedef struct _vrrp_sgroup { const char *gname; /* Group name */ const vector_t *iname; /* Set of VRRP instances in this group, only used during initialisation */ list_head_t vrrp_instances; /* vrrp_t - VRRP instances */ unsigned num_member_fault; /* Number of members of group in fault state */ unsigned num_member_init; /* Number of members of group in pending state */ int state; /* current stable state */ bool state_same_at_reload; /* State prior to reload */ bool sgroup_tracking_weight; /* Use floating priority and scripts * Used if need different priorities needed on a track object in a sync group. * It probably won't work properly. */ list_head_t track_ifp; /* tracked_if_t - Interface state we monitor */ list_head_t track_script; /* Script state we monitor */ list_head_t track_file; /* tracked_file_monitor_t - Files whose value we monitor */ #ifdef _WITH_TRACK_PROCESS_ list_head_t track_process; /* tracked_process_t - Processes we monitor */ #endif #ifdef _WITH_BFD_ list_head_t track_bfd; /* tracked_bfd_t - BFD instances we monitor */ #endif /* State transition notification */ bool notify_exec; notify_script_t *script_backup; notify_script_t *script_master; notify_script_t *script_fault; notify_script_t *script_stop; notify_script_t *script; int smtp_alert; int last_email_state; int notify_priority_changes; /* linked list member */ list_head_t e_list; } vrrp_sgroup_t; /* Statistics */ typedef struct _vrrp_stats { uint64_t advert_rcvd; uint32_t advert_sent; uint32_t become_master; uint32_t release_master; uint64_t packet_len_err; uint64_t advert_interval_err; uint64_t ip_ttl_err; uint64_t invalid_type_rcvd; uint64_t addr_list_err; uint32_t invalid_authtype; #ifdef _WITH_VRRP_AUTH_ uint32_t authtype_mismatch; uint32_t auth_failure; #endif uint64_t pri_zero_rcvd; uint64_t pri_zero_sent; #ifdef _WITH_SNMP_RFC_ uint32_t chk_err; uint32_t vers_err; uint32_t vrid_err; timeval_t uptime; #ifdef _WITH_SNMP_RFCV3_ uint32_t master_reason; uint32_t next_master_reason; uint32_t proto_err_reason; #endif #endif } vrrp_stats; #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ /* Whether we are using v1.3.6 and earlier VRRPv3 unicast checksums */ typedef enum chksum_compatibility { CHKSUM_COMPATIBILITY_NONE, /* Default setting, will revert to old if receive advert with old */ CHKSUM_COMPATIBILITY_NEVER, /* Do not auto set old checksum mode */ CHKSUM_COMPATIBILITY_MIN_COMPAT, /* Values before this are new chksum, values after are old */ CHKSUM_COMPATIBILITY_CONFIG, /* Configuration specifies old chksum */ CHKSUM_COMPATIBILITY_AUTO, /* Use old chksum mode due to received advert with old mode */ } chksum_compatibility_t; #endif #ifdef _CHECKSUM_DEBUG_ typedef struct { uint32_t last_rx_checksum; uint32_t last_tx_checksum; uint8_t last_rx_priority; uint8_t last_tx_priority; in_addr_t last_rx_from; bool sent_to; bool received_from; } checksum_check_t; #endif typedef struct _unicast_peer_t { sockaddr_t address; #ifdef _CHECKSUM_DEBUG_ checksum_check_t chk; #endif unsigned char min_ttl; unsigned char max_ttl; /* Linked list member */ list_head_t e_list; } unicast_peer_t; typedef enum vrrp_fault_fl { VRRP_FAULT_FL_TRACKER = 0, VRRP_FAULT_FL_INTERFACE_DOWN, #ifdef _HAVE_VRRP_VMAC_ VRRP_FAULT_FL_BASE_INTERFACE_DOWN, #endif /* _HAVE_VRRP_VMAC_ */ VRRP_FAULT_FL_DUPLICATE_VRID, VRRP_FAULT_FL_NO_SOURCE_IP, VRRP_FAULT_FL_CONFIG_ERROR, } vrrp_fault_fl_t; /* parameters per virtual router -- rfc2338.6.1.2 */ typedef struct _vrrp_t { sa_family_t family; /* AF_INET|AF_INET6 */ const char *iname; /* Instance Name */ vrrp_sgroup_t *sync; /* Sync group we belong to */ vrrp_stats *stats; /* Statistics */ interface_t *ifp; /* Interface we belong to */ #ifdef _HAVE_VRF_ const interface_t *vrf_ifp; /* VRF interface if no interface specified */ #endif unsigned strict_mode; /* Enforces strict VRRP compliance */ unsigned long flags; vrrp_rlflags_t rlflags; /* Flags for rate-limiting log messages */ #ifdef _HAVE_VRRP_VMAC_ char vmac_ifname[IFNAMSIZ]; /* Name of VRRP VMAC interface */ u_char ll_addr[ETH_ALEN]; /* Override MAC address */ uint32_t vmac_group; /* interface group for VMAC/ipvlan */ #ifdef _HAVE_VRRP_IPVLAN_ struct _ip_address *ipvlan_addr; /* Address to configure on an ipvlan interface */ int ipvlan_type; /* Bridge, private or VEPA mode */ #endif interface_t *configured_ifp; /* Interface the configuration says we are on */ #endif list_head_t track_ifp; /* tracked_if_t - Interface state we monitor */ list_head_t track_script; /* tracked_sc_t - Script state we monitor */ list_head_t track_file; /* tracked_file_monitor_t - Files whose value we monitor */ #ifdef _WITH_TRACK_PROCESS_ list_head_t track_process; /* tracked_process_t - Processes we monitor */ #endif #ifdef _WITH_BFD_ list_head_t track_bfd; /* tracked_bfd_t - BFD instance state we monitor */ #endif unsigned num_config_faults; /* Number of configuration errors */ unsigned num_track_fault; /* Number of trackers (script, bfd...) in fault state */ unsigned long flags_if_fault; /* Flags of interface fault */ unsigned num_script_init; /* Number of scripts in init state */ bool notifies_sent; /* Set when initial notifies have been sent */ bool multicast_pkt; /* Last IPv6 packet received was multicast */ sockaddr_t saddr; /* Src IP address to use in VRRP IP header */ sockaddr_t pkt_saddr; /* Src IP address received in VRRP IP header */ sockaddr_t mcast_daddr; /* Multicast destination address */ int rx_ttl_hl; /* Received TTL/hop limit returned */ list_head_t unicast_peer; /* unicast_peer_t - peers to send unicast advert to */ int ttl; /* TTL to send packet with if unicasting */ #ifdef _WITH_UNICAST_CHKSUM_COMPAT_ chksum_compatibility_t unicast_chksum_compat; /* Whether v1.3.6 and earlier chksum is used */ #endif #ifdef _CHECKSUM_DEBUG_ checksum_check_t chk; #endif sockaddr_t master_saddr; /* Store last heard Master address */ uint8_t master_priority; /* Store last heard priority */ timeval_t last_transition; /* Store transition time */ unsigned garp_delay; /* Delay to launch gratuitous ARP */ timeval_t garp_refresh; /* Next scheduled gratuitous ARP refresh */ unsigned garp_rep; /* gratuitous ARP repeat value */ unsigned garp_refresh_rep; /* refresh gratuitous ARP repeat value */ unsigned garp_lower_prio_delay; /* Delay to second set or ARP messages */ unsigned garp_lower_prio_rep; /* Number of ARP messages to send at a time */ unsigned down_timer_adverts; /* Number of adverts missed before backup takes over as master */ unsigned lower_prio_no_advert; /* Don't send advert after lower prio advert received */ unsigned higher_prio_send_advert; /* Send advert after higher prio advert received */ unsigned owner_ignore_adverts; /* Set if an address owner should ignore received adverts */ #ifdef _HAVE_VRRP_VMAC_ timeval_t vmac_garp_intvl; /* Interval between GARPs on each VMAC */ #endif uint8_t vrid; /* virtual id. from 1(!) to 255 */ uint8_t base_priority; /* configured priority value */ uint8_t effective_priority; /* effective priority value */ int total_priority; /* base_priority +/- track_script, track_interface, track_bfd and track_file weights. effective_priority is this within the range [1,254]. */ uint8_t highest_other_priority; /* Used for timer_expired_backup */ bool vipset; /* All the vips are set ? */ list_head_t vip; /* ip_address_t - list of virtual ip addresses */ unsigned vip_cnt; /* size of vip list */ list_head_t evip; /* ip_address_t - list of protocol excluded VIPs. * Those VIPs will not be presents into the * VRRP adverts */ list_head_t vroutes; /* ip_route_t - list of virtual routes */ list_head_t vrules; /* ip_rule_t - list of virtual rules */ unsigned adver_int; /* locally configured delay between advertisements*/ unsigned master_adver_int; /* In v3, when we become BACKUP, we use the MASTER's * adver_int. If we become MASTER again, we use the * value we were originally configured with. * In v2, this will always be the configured adver_int. */ timeval_t last_advert_sent; /* Time of sending last advert */ size_t kernel_rx_buf_size; /* Socket receive buffer size */ unsigned rogue_counter; /* Used if we are address owner and another */ thread_ref_t rogue_timer_thread; /* system advertises it is the address owner */ unsigned rogue_adver_int; #ifdef _WITH_FIREWALL_ unsigned accept; /* Allow the non-master owner to process * the packets destined to VIP. */ bool firewall_rules_set; /* Firewall drop rules set to VIP list ? */ #endif unsigned long preempt_delay; /* Seconds*TIMER_HZ after startup until * preemption based on higher prio over lower * prio is allowed. 0 means no delay. */ timeval_t preempt_time; /* Time after which preemption can happen */ int state; /* internal state (init/backup/master/fault) */ #ifdef _WITH_SNMP_VRRP_ int configured_state; /* the configured state of the instance */ #endif int wantstate; /* user explicitly wants a state (back/mast) */ bool reload_master; /* set if the instance is a master being reloaded */ sock_t *sockets; /* In and out socket descriptors */ int debug; /* Debug level 0-4 */ int version; /* VRRP version (2 or 3) */ /* State transition notification */ int smtp_alert; int last_email_state; bool notify_exec; bool notify_deleted; notify_script_t *script_backup; notify_script_t *script_master; notify_script_t *script_fault; notify_script_t *script_stop; notify_script_t *script_deleted; notify_script_t *script_master_rx_lower_pri; notify_script_t *script; int notify_priority_changes; /* rfc2338.6.2 */ uint32_t ms_down_timer; timeval_t sands; /* Sending buffer */ char *send_buffer; /* Allocated send buffer */ size_t send_buffer_size; uint32_t ipv4_csum; /* Checksum ip IPv4 pseudo header for VRRPv3 */ #if defined _WITH_VRRP_AUTH_ /* Authentication data (only valid for VRRPv2) */ uint8_t auth_type; /* authentification type. VRRP_AUTH_* */ uint8_t auth_data[8]; /* authentification data */ /* IPSEC AH counter def (only valid for VRRPv2) --rfc2402.3.3.2 */ seq_counter_t ipsecah_counter; #endif /* * To have my own ip_id creates collision with kernel ip->id * but it should be ok because the packets are unlikely to be * fragmented (they are non routable and small) * This packet isnt routed, i can check the outgoing MTU * to warn the user only if the outoing mtu is too small */ int ip_id; /* RB tree on a sock_t for receiving data */ rb_node_t rb_vrid; /* RB tree on a sock_t for vrrp sands */ rb_node_t rb_sands; /* Sync group list member */ list_head_t s_list; /* vrrp_sgroup_t->vrrp_instances */ /* Linked list member */ list_head_t e_list; } vrrp_t; /* VRRP state machine -- rfc2338.6.4 */ #define VRRP_STATE_INIT 0 /* rfc2338.6.4.1 */ #define VRRP_STATE_BACK 1 /* rfc2338.6.4.2 */ #define VRRP_STATE_MAST 2 /* rfc2338.6.4.3 */ #define VRRP_STATE_FAULT 3 /* internal */ #define VRRP_STATE_DELETED 97 /* internal */ #define VRRP_STATE_STOP 98 /* internal */ #define VRRP_EVENT_MASTER_RX_LOWER_PRI 1000 /* Dummy state for sending event notify */ #define VRRP_EVENT_MASTER_PRIORITY_CHANGE 1001 /* Dummy state for sending event notify */ #define VRRP_EVENT_BACKUP_PRIORITY_CHANGE 1002 /* Dummy state for sending event notify */ /* VRRP packet handling */ enum vrrp_packet_status { VRRP_PACKET_OK, VRRP_PACKET_KO, VRRP_PACKET_DROP, VRRP_PACKET_NULL, VRRP_PACKET_OTHER /* Multiple VRRP on LAN, Identify "other" VRRP */ }; /* VRRP Packet fixed length */ #define VRRP_AUTH_LEN 8 #define VRRP_VIP_TYPE (1 << 0) #define VRRP_EVIP_TYPE (1 << 1) /* We have to do some reduction of the calculation for VRRPv3 in order not to overflow a uint32; 625 / 16 == TIMER_CENTI_HZ / 256 */ #define VRRP_TIMER_SKEW_CALC(svr, pri_val) ((svr)->version == VRRP_VERSION_3 ? (((pri_val) * ((svr)->master_adver_int / TIMER_CENTI_HZ) * 625U) / 16U) : ((pri_val) * TIMER_HZ/256U)) #define VRRP_TIMER_SKEW(svr) VRRP_TIMER_SKEW_CALC(svr, 256U - ((svr)->base_priority == VRRP_PRIO_OWNER ? VRRP_PRIO_OWNER : (svr)->effective_priority)) #define VRRP_TIMER_SKEW_MIN(svr) VRRP_TIMER_SKEW_CALC(svr, 1) #define VRRP_MS_DOWN_TIMER(XX) ((XX)->down_timer_adverts * (XX)->master_adver_int + VRRP_TIMER_SKEW(XX)) #define VRRP_VIP_ISSET(V) ((V)->vipset) #define VRRP_MIN(a, b) ((a) < (b)?(a):(b)) #define VRRP_MAX(a, b) ((a) > (b)?(a):(b)) #ifdef _HAVE_VRRP_VMAC_ #define VRRP_CONFIGURED_IFP(V) ((V)->configured_ifp) #else #define VRRP_CONFIGURED_IFP(V) ((V)->ifp) #endif #define VRRP_PKT_SADDR(V) (((V)->saddr.ss_family) ? ((struct sockaddr_in *) &(V)->saddr)->sin_addr.s_addr : IF_ADDR(VRRP_CONFIGURED_IFP(V))) #define VRRP_ISUP(V) (!(V)->flags_if_fault) /* Configuration summary flags */ extern bool have_ipv4_instance; extern bool have_ipv6_instance; #ifdef _NETWORK_TIMESTAMP_ extern bool do_network_timestamp; #endif #ifdef _CHECKSUM_DEBUG_ extern bool do_checksum_debug; #endif /* prototypes */ extern void clear_summary_flags(void); extern size_t vrrp_adv_len(const vrrp_t *) __attribute__ ((pure)); extern const vrrphdr_t *vrrp_get_header(sa_family_t, const char *, size_t); extern void open_sockpool_socket(sock_t *); extern int new_vrrp_socket(vrrp_t *); extern void vrrp_send_adv(vrrp_t *, uint8_t); extern void vrrp_send_link_update(vrrp_t *, unsigned); extern void vrrp_send_vmac_update(vrrp_t *); extern void add_vrrp_to_interface(vrrp_t *, interface_t *, int, bool, bool, track_t); extern void del_vrrp_from_interface(vrrp_t *, interface_t *); extern bool vrrp_state_master_rx(vrrp_t *, const vrrphdr_t *, const char *, ssize_t); extern void vrrp_state_master_tx(vrrp_t *); extern void vrrp_state_backup(vrrp_t *, const vrrphdr_t *, const char *, ssize_t); extern void vrrp_state_goto_master(vrrp_t *); extern void vrrp_state_leave_master(vrrp_t *, bool); extern void vrrp_state_leave_fault(vrrp_t *); extern bool vrrp_complete_init(void); extern void vrrp_restore_interfaces_startup(void); extern void restore_vrrp_interfaces(void); extern void shutdown_vrrp_instances(void); extern void clear_diff_vrrp(void); extern void clear_diff_script(void); extern void set_previous_sync_group_states(void); #ifdef _WITH_BFD_ extern void clear_diff_bfd(void); #endif extern void vrrp_restore_interface(vrrp_t *, bool, bool); #ifdef THREAD_DUMP extern void register_vrrp_fifo_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/pidfile.h0000664000175000017500000000365214655677312014662 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: pidfile.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _PIDFILE_H #define _PIDFILE_H /* system include */ #include #include #include "utils.h" /* lock pidfile */ #define KEEPALIVED_PID_DIR RUNSTATEDIR "/" PACKAGE "/" #define KEEPALIVED_PID_FILE PACKAGE #ifdef _WITH_VRRP_ #define VRRP_PID_FILE "vrrp" #endif #ifdef _WITH_LVS_ #define CHECKERS_PID_FILE "checkers" #endif #ifdef _WITH_BFD_ #define BFD_PID_FILE "bfd" #endif #define PID_EXTENSION ".pid" #define RELOAD_EXTENSION ".reload" typedef struct pidfile { const char * path; bool free_path; int fd; } pidfile_t; extern const char *pid_directory; /* Prototypes */ extern void create_pid_dir(void); extern void remove_pid_dir(void); extern char *make_pidfile_name(const char *, const char *, const char *); extern void pidfile_close(pidfile_t *, bool); extern bool pidfile_write(pidfile_t *); extern void pidfile_rm(pidfile_t *); extern void close_other_pidfiles(void); extern bool keepalived_running(unsigned long); #endif keepalived-2.3.3/keepalived/include/vrrp_sock.h0000664000175000017500000000327314770042064015241 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Dynamic data structure definition. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_SOCK_H #define _VRRP_SOCK_H /* system includes */ #include #include #include /* local includes */ #include "scheduler.h" #include "vrrp_if.h" #include "sockaddr.h" /* * Our instance dispatcher use a socket pool. * That way we handle VRRP protocol type per * physical interface. */ typedef struct _sock { sa_family_t family; int proto; interface_t *ifp; #ifdef _HAVE_VRF_ const interface_t *vrf_ifp; #endif sockaddr_t *unicast_src; sockaddr_t *mcast_daddr; int fd_in; int fd_out; int rx_buf_size; thread_ref_t thread; rb_root_t rb_vrid; rb_root_cached_t rb_sands; /* Linked list member */ list_head_t e_list; } sock_t; #endif keepalived-2.3.3/keepalived/include/check_tcp.h0000664000175000017500000000224413442471246015154 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_tcp.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _CHECK_TCP_H #define _CHECK_TCP_H /* Prototypes defs */ extern void install_tcp_check_keyword(void); #ifdef THREAD_DUMP extern void register_check_tcp_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/check_parser.h0000664000175000017500000000255314227044032015654 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_parser.c include file. * * Author: Alexandre Cassen, * * 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-2016 Alexandre Cassen, */ #ifndef _CHECK_PARSER_H #define _CHECK_PARSER_H /* Global include */ #include /* local include */ #include "vector.h" #include "check_data.h" extern virtual_server_t *current_vs; extern real_server_t *current_rs; extern virtual_server_group_t *current_vsg; /* Prototypes */ extern const vector_t *check_init_keywords(void); extern void init_check_keywords(bool); #endif keepalived-2.3.3/keepalived/include/config_notify.h0000664000175000017500000000262314104604760016062 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Config read completion notification include file. * * Author: Quentin Armitage, * * 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) 2021-2021 Alexandre Cassen, */ #ifndef _CONFIG_NOTIFY_H #define _CONFIG_NOTIFY_H #include "config.h" #include #include extern void queue_reload(void); extern void open_config_read_fd(void); extern void notify_config_read(void); #ifndef _ONE_PROCESS_DEBUG_ extern void save_config(bool, const char *, void(*)(FILE *)); #endif #ifdef THREAD_DUMP extern void register_config_notify_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/reload_monitor.h0000664000175000017500000000227713612173246016252 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: reload_monitor.c include file. * * Author: Quentin Armitage * * 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) 2020-2020 Alexandre Cassen, */ #ifndef _RELOAD_MONITOR_H #define _RELOAD_MONITOR_H extern void start_reload_monitor(void); extern void stop_reload_monitor(void); #ifdef THREAD_DUMP extern void register_reload_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/nftables.h0000664000175000017500000001137114034361354015024 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: nftables.h * * Author: Quentin Armitage, * * 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) 2020-2020 Alexandre Cassen, */ /* Up to commit 0ec6c01f this used libnftnl/libmnl, but that had overheads, * and constructing the netlink packets directly works just as well. */ #ifndef _CORE_NFTABLES_H #define _CORE_NFTABLES_H #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_NFTNL_UDATA #include #endif #include #include #include #include #include #include //#include #include #include #include #include "vrrp_nftables.h" #include "logger.h" #include "vrrp.h" #include "vrrp_ipaddress.h" #include "global_data.h" #include "list_head.h" #include "utils.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_firewall.h" #endif /* nft supports ifnames in sets from commit 8c61fa7 (release v0.8.3, libnftnl v1.0.9 (but 0.8.2 also uses that, 0.8.4 uses v1.1.0)) */ /* nft supports concatenated ranges from commit 8ac2f3b (release v0.9.4, libnftnl v1.1.6 and kernel 5.6) */ /* The following are from nftables source code (include/datatype.h) * and are used for it to determine how to display the entries in * the set. */ #define NFT_TYPE_STRING 5 #define NFT_TYPE_IPADDR 7 #define NFT_TYPE_IP6ADDR 8 #define NFT_TYPE_INET_SERVICE 13 #define NFT_TYPE_MARK 19 #define NFT_TYPE_IFINDEX 20 #define NFT_TYPE_ICMPV6_TYPE 29 #define NFT_TYPE_IFNAME 41 #define NFT_TYPE_BITS 6 #define NFT_TYPE_MASK ((1 << NFT_TYPE_BITS) - 1) /* For kernels < 4.1 */ #ifndef NFT_TABLE_MAXNAMELEN #define NFT_TABLE_MAXNAMELEN 32 #endif #ifdef HAVE_NFTNL_UDATA /* This should be declared in /usr/include/libnftnl/udata.h */ enum byteorder { BYTEORDER_INVALID, BYTEORDER_HOST_ENDIAN, BYTEORDER_BIG_ENDIAN, }; #ifndef NFTNL_UDATA_SET_MAX /* libnftnl declared this from v1.1.3 */ enum udata_set_type { NFTNL_UDATA_SET_KEYBYTEORDER, NFTNL_UDATA_SET_DATABYTEORDER, NFTNL_UDATA_SET_MERGE_ELEMENTS, __NFTNL_UDATA_SET_MAX, }; /* #define NFTNL_UDATA_SET_MAX (__NFTNL_UDATA_SET_MAX - 1) */ #endif #endif /* Local definitions */ #define NO_REG (NFT_REG_MAX+1) extern struct mnl_socket *nl; extern uint32_t seq; #ifdef _WITH_VRRP_ extern void exchange_nl_msg_single(struct nlmsghdr *, int (*)(const struct nlmsghdr *, void *), bool *); #endif extern void my_mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *); extern void add_payload(struct nftnl_rule *, uint32_t, uint32_t, uint32_t, uint32_t); extern void add_meta(struct nftnl_rule *, uint32_t, uint32_t); #ifdef _WITH_LVS_ extern void add_meta_sreg(struct nftnl_rule *, uint32_t, uint32_t); #endif extern void add_lookup(struct nftnl_rule *, uint32_t, uint32_t, const char *, uint32_t, bool); #if defined _WITH_VRRP_ && defined _WITH_NFTABLES_ && HAVE_DECL_NFTA_DUP_MAX && defined _HAVE_VRRP_VMAC_ extern void add_dup(struct nftnl_rule *, uint32_t, uint32_t); #endif extern void add_immediate_verdict(struct nftnl_rule *, uint32_t, const char *); extern void add_cmp(struct nftnl_rule *, uint32_t, uint32_t, const void *, uint32_t); #ifdef _WITH_VRRP_ extern void add_bitwise(struct nftnl_rule *, uint32_t, uint32_t, uint32_t, const void *, const void *); #endif extern void add_counter(struct nftnl_rule *); extern struct nftnl_table * table_add_parse(uint16_t, const char *); extern struct nftnl_chain * chain_add_parse(const char *, const char *); extern struct nftnl_set *setup_set(uint8_t, const char *, const char *, int, int, int); extern struct mnl_nlmsg_batch * nft_start_batch(void); extern void nft_end_batch(struct mnl_nlmsg_batch *, bool); extern void nft_discard_batch(struct mnl_nlmsg_batch *); extern int set_nf_ifname_type(void); #endif keepalived-2.3.3/keepalived/include/libnl_link.h0000664000175000017500000000723413663215160015346 /* * libnl_link: Handle dynamic linking to netlink libraries * * Authors: P. Quentin Armitage * * 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) 2017-2017 Alexandre Cassen, */ #ifndef _LIBNL_LINK_H #define _LIBNL_LINK_H #include "config.h" #include #include #include #ifdef LIBIPVS_USE_NL #include #include #endif /* The addresses of the functions we want */ extern struct nl_sock * (*nl_socket_alloc_addr)(void); extern void (*nl_socket_free_addr)(struct nl_sock *); #ifdef LIBIPVS_USE_NL extern int (*genl_connect_addr)(struct nl_sock *); extern int (*genl_ctrl_resolve_addr)(struct nl_sock *, const char *); extern int (*genlmsg_parse_addr)(struct nlmsghdr *, int, struct nlattr **, int, struct nla_policy *); extern void * (*genlmsg_put_addr)(struct nl_msg *, uint32_t, uint32_t, int, int, int, uint8_t, uint8_t); extern int (*nla_nest_end_addr)(struct nl_msg *, struct nlattr *); extern struct nlattr * (*nla_nest_start_addr)(struct nl_msg *, int); extern int (*nla_put_daddr)(struct nl_msg *, int, int, const void *); extern struct nl_msg * (*nlmsg_alloc_addr)(void); extern void (*nlmsg_free_addr)(struct nl_msg *); extern struct nlmsghdr * (*nlmsg_hdr_addr)(struct nl_msg *); extern int (*nl_recvmsgs_default_addr)(struct nl_sock *); extern int (*nl_send_auto_addr)(struct nl_sock *, struct nl_msg *); extern int (*nl_socket_modify_cb_addr)(struct nl_sock *, enum nl_cb_type, enum nl_cb_kind, nl_recvmsg_msg_cb_t, void *); extern int (*nl_socket_modify_err_cb_addr)(struct nl_sock *, enum nl_cb_kind, nl_recvmsg_err_cb_t, void *); #ifdef _HAVE_LIBNL3_ extern void * (*nla_data_addr)(const struct nlattr *); #ifdef NLA_PUT_S32 extern int32_t (*nla_get_s32_addr)(const struct nlattr *); #endif extern char * (*nla_get_string_addr)(const struct nlattr *); extern uint16_t (*nla_get_u16_addr)(const struct nlattr *); extern uint32_t (*nla_get_u32_addr)(const struct nlattr *); extern uint64_t (*nla_get_u64_addr)(const struct nlattr *); extern int (*nla_memcpy_addr)(void *, const struct nlattr *, int); extern int (*nla_parse_nested_addr)(struct nlattr **, int, struct nlattr *, struct nla_policy *); #endif #endif /* We can make it look as though normal linking is being used */ #define nl_socket_alloc (*nl_socket_alloc_addr) #define nl_socket_free (*nl_socket_free_addr) #ifdef LIBIPVS_USE_NL #define genl_connect (*genl_connect_addr) #define genl_ctrl_resolve (*genl_ctrl_resolve_addr) #define genlmsg_parse (*genlmsg_parse_addr) #define genlmsg_put (*genlmsg_put_addr) #define nla_nest_end (*nla_nest_end_addr) #define nla_nest_start (*nla_nest_start_addr) #define nla_put (*nla_put_daddr) #define nlmsg_alloc (*nlmsg_alloc_addr) #define nlmsg_free (*nlmsg_free_addr) #define nlmsg_hdr (*nlmsg_hdr_addr) #define nl_recvmsgs_default (*nl_recvmsgs_default_addr) #define nl_send_auto (*nl_send_auto_addr) #define nl_socket_modify_cb (*nl_socket_modify_cb_addr) #define nl_socket_modify_err_cb (*nl_socket_modify_err_cb_addr) #ifdef _HAVE_LIBNL3_ #define nla_data (*nla_data_addr) #ifdef NLA_PUT_S32 #define nla_get_s32 (*nla_get_s32_addr) #endif #define nla_get_string (*nla_get_string_addr) #define nla_get_u16 (*nla_get_u16_addr) #define nla_get_u32 (*nla_get_u32_addr) #define nla_get_u64 (*nla_get_u64_addr) #define nla_memcpy (*nla_memcpy_addr) #define nla_parse_nested (*nla_parse_nested_addr) #endif #endif extern bool libnl_init(void); #endif keepalived-2.3.3/keepalived/include/bfd_data.h0000664000175000017500000000435114706175715014764 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: bfd_data.c include file. * * Author: Ilya Voronin, * * 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) 2015-2023 Alexandre Cassen, */ #ifndef _BFD_DATA_H #define _BFD_DATA_H #include #include #include "bfd.h" #include "sockaddr.h" typedef struct _bfd_data { list_head_t bfd; /* bfd_t - BFD instances */ int fd_in; /* Input socket fd */ int multihop_fd_in; /* Input socket for multihop */ thread_ref_t thread_in; /* Input socket thread */ } bfd_data_t; #define BFD_BUFFER_SIZE 32 /* Global Vars exported */ extern bfd_data_t *bfd_data; extern bfd_data_t *old_bfd_data; extern char *bfd_buffer; extern bfd_t *alloc_bfd(const char *); extern void free_bfd(bfd_t *); extern bfd_data_t *alloc_bfd_data(void); extern void dump_bfd_data(FILE *, const bfd_data_t *); #ifndef _ONE_PROCESS_DEBUG_ extern void dump_bfd_data_global(FILE *); #endif extern void bfd_print_data(void); extern void free_bfd_data(bfd_data_t **); extern void bfd_complete_init(void); extern void alloc_bfd_buffer(void); extern void free_bfd_buffer(void); extern bfd_t *find_bfd_by_addr(const sockaddr_t *, const sockaddr_t *, bool) __attribute__ ((pure)); extern bfd_t *find_bfd_by_discr(const uint32_t) __attribute__ ((pure)); extern bfd_t *find_bfd_by_name(const char *) __attribute__ ((pure)); extern uint32_t rand_intv(uint32_t, uint32_t); extern uint32_t bfd_get_random_discr(bfd_data_t *); #endif /* _BFD_DATA_H */ keepalived-2.3.3/keepalived/include/snmp.h0000664000175000017500000000604514117170030014174 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: snmp.c include file. * * Authors: Vincent Bernat * * 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, */ #ifndef _SNMP_H #define _SNMP_H #include "config.h" #include #include #define USING_AGENTX_SUBAGENT_MODULE #include #include #include #include #if HAVE_NET_SNMP_AGENT_UTIL_FUNCS_H #include #else /* The above header may be buggy. We just need those two functions. */ int header_simple_table(struct variable *, oid *, size_t *, int, size_t *, WriteMethod **, int); int header_generic(struct variable *, oid *, size_t *, int, size_t *, WriteMethod **); #endif #undef FREE #include "list_head.h" #define SNMP_DEFAULT_NETWORK_SOCKET "udp:localhost:705" #define KEEPALIVED_OID 1, 3, 6, 1, 4, 1, 9586, 100, 5 #define SNMPTRAP_OID 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 #define GLOBAL_OID {KEEPALIVED_OID, 1} /* A TruthValue has value 1 for true and 2 for false */ #define SNMP_TruthValue(x) (1 + !(x)) /* A TruthValue has value 1 for AF_INET and 2 for AF_INET6 */ #define SNMP_InetAddressType(x) ((x) == AF_INET ? 1 : 2) typedef union { unsigned char uc; unsigned long u; long s; } longret_t; /* The type of a FindVarMethod function should be const unsigned char *, but * it is declared unsigned char *. We use this simple work around to be able * to return const char *, and other types. */ typedef union { const char *cp; u_char *p; } snmp_ret_t; extern unsigned long snmp_scope(int ) __attribute__ ((const)); extern list_head_t *snmp_header_list_head_table(struct variable *, oid *, size_t *, int, size_t *, WriteMethod **, list_head_t *); extern list_head_t *snmp_find_element(struct variable *, oid *, size_t *, int, size_t *, WriteMethod **, list_head_t *, size_t, size_t); extern void snmp_agent_init(const char *, bool); extern void snmp_register_mib(oid *, size_t, const char *, struct variable *, size_t, size_t); extern void snmp_unregister_mib(oid *, size_t); extern void snmp_agent_close(bool); #ifdef THREAD_DUMP extern void register_snmp_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/sanitizer.h0000664000175000017500000000211114645450427015236 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Compiler sanitizer debugging include file * * Author: Alexandre Cassen, * * 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) 2024-2024 Alexandre Cassen, */ #ifndef _SANITIZER_H #define _SANITIZER_H extern void sanitizer_init(void); #endif keepalived-2.3.3/keepalived/include/check_misc.h0000664000175000017500000000347714661620303015324 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_misc.c include file. * * Author: Alexandre Cassen, * Eric Jarman, * * 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, */ #ifndef _CHECK_MISC_H #define _CHECK_MISC_H /* system includes */ #include #include /* user includes */ #include "notify.h" #include "keepalived_magic.h" /* Checker argument structure */ typedef struct _misc_checker { notify_script_t script; /* The script details */ unsigned long timeout; bool dynamic; /* false: old-style, true: exit code from checker affects weight */ script_state_t state; /* current state of script */ unsigned last_exit_code; /* The last exit code of the script */ timeval_t last_ran; /* Time script last ran */ } misc_checker_t; /* Prototypes defs */ extern void install_misc_check_keyword(void); extern unsigned check_misc_script_security(magic_t); #ifdef THREAD_DUMP extern void register_check_misc_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/keepalived_netlink.h0000664000175000017500000001124314300450516017054 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: keepalived_netlink.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_NETLINK_H #define _VRRP_NETLINK_H 1 #include "config.h" /* global includes */ #include #include #include #include #include /* local includes */ #include "scheduler.h" #ifdef _WITH_VRRP_ #include "vrrp_if.h" #endif #include "align.h" /* types definitions */ typedef struct _nl_handle { int fd; uint32_t nl_pid; __u32 seq; thread_ref_t thread; } nl_handle_t; /* Define types */ #ifndef NLMSG_TAIL #define NLMSG_TAIL(nmsg) ((void *)(((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) #endif #define RTA_TAIL(rta) PTR_CAST(struct rtattr, (char *)(rta) + RTA_ALIGN((rta)->rta_len)) /* Global vars exported */ #ifdef _WITH_VRRP_ extern nl_handle_t nl_cmd; /* Command channel */ extern int netlink_error_ignore; /* If we get this error, ignore it */ #endif #ifdef _NETLINK_TIMERS_ extern bool do_netlink_timers; #endif /* prototypes */ #ifdef _NETLINK_TIMERS_ extern void report_and_clear_netlink_timers(const char *); #endif extern int addattr_l(struct nlmsghdr *, size_t, unsigned short, const void *, size_t) GCC_LTO_NOINLINE; extern int addattr_l2(struct nlmsghdr *, size_t, unsigned short, const void *, size_t, const void *, size_t); extern int addraw_l(struct nlmsghdr *, size_t, const void *, size_t); static inline int addattr8(struct nlmsghdr *n, size_t maxlen, unsigned short type, uint8_t data) { return addattr_l(n, maxlen, type, &data, sizeof data); } static inline int addattr16(struct nlmsghdr *n, size_t maxlen, unsigned short type, uint16_t data) { return addattr_l(n, maxlen, type, &data, sizeof data); } static inline int addattr32(struct nlmsghdr *n, size_t maxlen, unsigned short type, uint32_t data) { return addattr_l(n, maxlen, type, &data, sizeof data); } static inline int addattr64(struct nlmsghdr *n, size_t maxlen, unsigned short type, uint64_t data) { return addattr_l(n, maxlen, type, &data, sizeof(data)); } #ifdef _WITH_VRRP_ extern size_t rta_addattr_l(struct rtattr *, size_t, unsigned short, const void *, size_t); extern size_t rta_addattr_l2(struct rtattr *, size_t, unsigned short, const void *, size_t, const void*, size_t); static inline size_t rta_addattr8(struct rtattr *rta, size_t maxlen, unsigned short type, uint8_t data) { return rta_addattr_l(rta, maxlen, type, &data, sizeof data); } static inline size_t rta_addattr16(struct rtattr *rta, size_t maxlen, unsigned short type, uint16_t data) { return rta_addattr_l(rta, maxlen, type, &data, sizeof data); } static inline size_t rta_addattr32(struct rtattr *rta, size_t maxlen, unsigned short type, uint32_t data) { return rta_addattr_l(rta, maxlen, type, &data, sizeof data); } static inline size_t rta_addattr64(struct rtattr *rta, size_t maxlen, unsigned short type, uint64_t data) { return rta_addattr_l(rta, maxlen, type, &data, sizeof data); } extern struct rtattr *rta_nest(struct rtattr *, size_t, unsigned short); extern size_t rta_nest_end(struct rtattr *, struct rtattr *); extern ssize_t netlink_talk(nl_handle_t *, struct nlmsghdr *); extern int netlink_interface_lookup(char *); extern void kernel_netlink_poll(void); extern void process_if_status_change(interface_t *); #endif extern void kernel_netlink_set_recv_bufs(void); #ifdef _WITH_VRRP_ extern void set_extra_netlink_monitoring(bool, bool, bool, bool); #endif extern void kernel_netlink_init(void); extern void cancel_kernel_netlink_threads(void); #if defined _WITH_VRRP_ || defined _WITH_LVS_ extern void kernel_netlink_read_interfaces(void); #endif extern void kernel_netlink_close(void); extern void kernel_netlink_close_monitor(void); extern void kernel_netlink_close_cmd(void); #ifdef THREAD_DUMP extern void register_keepalived_netlink_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/check_print.h0000664000175000017500000000224013442511450015506 /* * 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: check_print.c program include file. * * Author: Quentin Armitage * * 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) 2019-2019 Alexandre Cassen, */ #ifndef _CHECK_PRINT_H #define _CHECK_PRINT_H extern void check_print_data(void); #endif keepalived-2.3.3/keepalived/include/vrrp_iptables.h0000664000175000017500000000327313741620525016106 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_iptables.c include file. * * Author: Alexandre Cassen, * * 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-2018 Alexandre Cassen, */ #ifndef _VRRP_IPTABLES_H #define _VRRP_IPTABLES_H #include "config.h" /* global includes */ #include /* local includes */ #include "vrrp.h" #ifdef _HAVE_LIBIPSET_ #include "vrrp_ipset.h" #endif #include "vrrp_ipaddress.h" #include "vrrp_iptables.h" #define DEFAULT_IPTABLES_CHAIN_IN "INPUT" #define DEFAULT_IPTABLES_CHAIN_OUT "OUTPUT" /* prototypes */ extern void handle_iptable_rule_to_iplist(list_head_t *, list_head_t *, int, bool force); extern void handle_iptables_accept_mode(vrrp_t *, int, bool); #ifdef _HAVE_VRRP_VMAC_ extern void iptables_add_vmac(const interface_t *, int, bool); extern void iptables_remove_vmac(const interface_t *, int, bool); #endif extern void iptables_fini(void); #endif keepalived-2.3.3/keepalived/include/layer4.h0000664000175000017500000000616514115724516014436 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: layer4.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _LAYER4_H #define _LAYER4_H /* system includes */ #include #include #include #include /* local includes */ #include "scheduler.h" #include "sockaddr.h" enum connect_result { connect_error, connect_in_progress, connect_timeout, connect_fail, connect_success, connect_result_next }; /* connection options structure definition */ typedef struct _conn_opts { sockaddr_t dst; sockaddr_t bindto; char bind_if[IFNAMSIZ]; unsigned int connection_to; /* connection time-out */ #ifdef _WITH_SO_MARK_ unsigned int fwmark; /* to mark packets going out of the socket using SO_MARK */ #endif int last_errno; /* Errno from last call to connect */ } conn_opts_t; /* Prototypes defs */ #ifdef _WITH_LVS_ extern void set_buf(char *, size_t); extern enum connect_result socket_bind_connect(int, conn_opts_t *); #endif extern enum connect_result socket_connect(int, const sockaddr_t *); extern enum connect_result socket_state(thread_ref_t, thread_func_t, unsigned); #ifdef _WITH_LVS_ extern bool socket_connection_state(int, enum connect_result , thread_ref_t, thread_func_t , unsigned long, unsigned); #endif /* Backward compatibility */ #ifdef _WITH_LVS_ static inline enum connect_result tcp_bind_connect(int fd, conn_opts_t *co) { return socket_bind_connect(fd, co); } #endif static inline enum connect_result tcp_connect(int fd, const sockaddr_t *addr) { return socket_connect(fd, addr); } static inline enum connect_result tcp_socket_state(thread_ref_t thread, thread_func_t func, unsigned extra_flags) { return socket_state(thread, func, extra_flags); } #ifdef _WITH_LVS_ static inline bool tcp_connection_state(int fd, enum connect_result status, thread_ref_t thread, thread_func_t func, unsigned long timeout, unsigned flags) { return socket_connection_state(fd, status, thread, func, timeout, flags); } extern enum connect_result udp_bind_connect(int, conn_opts_t *, uint8_t *, uint16_t); extern enum connect_result udp_socket_state(int, thread_ref_t, uint8_t *, size_t *); extern bool udp_icmp_check_state(int, enum connect_result, thread_ref_t, thread_func_t, unsigned long); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_vmac_nm.h0000664000175000017500000000220114303133343015701 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_vmac_nm.c include file. * * Author: Quentin Armitage, * * 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) 2022-2022 Alexandre Cassen, */ #ifndef _VRRP_VMAC_NM_H #define _VRRP_VMAC_NM_H /* global includes */ /* local includes */ extern void set_vmac_unmanaged_nm(const char *); #endif keepalived-2.3.3/keepalived/include/vrrp_snmp.h0000664000175000017500000000570514227015654015263 /* * 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: vrrp_snmp.c program include file. * * Author: Vincent Bernat * * 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, */ #ifndef _VRRP_SNMP_H #define _VRRP_SNMP_H #include "config.h" #include "global_data.h" #ifdef _WITH_SNMP_RFC_ #include "timer.h" #endif #include "vrrp.h" #ifdef _WITH_SNMP_RFCV2_ enum rfcv2_trap_auth_error_type { invalidAuthType = 1, #ifdef _WITH_VRRP_AUTH_ authTypeMismatch, authFailure #endif }; #endif #ifdef _WITH_SNMP_RFCV3_ enum rfcv3_notify_proto_error_type { noError = 0, ipTtlError, versionError, checksumError, vrIdError }; /* RFC6527 isn't clear about the meaning of the following. However, * the only meaning I can see is that PRIORITY means became master * after receiving a priority 0 advert, preempted means that we were * receiving lower priority adverts or we are the address owner, and * so transitioned to master, and MASTER_NO_RESPONSE means that we * didn't receive any adverts for 3 * master advert interval + skew time. * However, it is possible the PRIORITY is meant to mean priority 255 * (i.e. the address owner), in which case we don't have a specific * reason for transition following receiving a priority 0 advert. */ enum rfcv3_master_reason_type { VRRPV3_MASTER_REASON_NOT_MASTER = 0, VRRPV3_MASTER_REASON_PRIORITY, VRRPV3_MASTER_REASON_PREEMPTED, VRRPV3_MASTER_REASON_MASTER_NO_RESPONSE }; #endif #ifdef _WITH_SNMP_RFC_ /* Global vars */ extern timeval_t snmp_vrrp_start_time; #endif /* Prototypes */ extern void vrrp_snmp_agent_init(const char *); extern void vrrp_snmp_agent_close(const data_t *); #ifdef _WITH_SNMP_VRRP_ extern void vrrp_snmp_instance_trap(vrrp_t *); extern void vrrp_snmp_group_trap(vrrp_sgroup_t *); #endif #ifdef _WITH_SNMP_RFCV2_ extern void vrrp_rfcv2_snmp_new_master_trap(vrrp_t *); extern void vrrp_rfcv2_snmp_auth_err_trap(vrrp_t *, struct in_addr, enum rfcv2_trap_auth_error_type); #endif #ifdef _WITH_SNMP_RFCV3_ extern void vrrp_rfcv3_snmp_new_master_notify(vrrp_t *); extern void vrrp_rfcv3_snmp_proto_err_notify(vrrp_t *); #endif #endif keepalived-2.3.3/keepalived/include/check_smtp.h0000664000175000017500000000355514661620303015351 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_smtp.c include file. * * Author: Alexandre Cassen, * Jeremy Rumpf, * * 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, */ #ifndef _CHECK_SMTP_H #define _CHECK_SMTP_H /* system includes */ #include /* local includes */ #include "scheduler.h" #include "check_api.h" #define SMTP_BUFF_MAX 512U typedef enum { SMTP_START, SMTP_HAVE_BANNER, SMTP_SENT_HELO, SMTP_RECV_HELO, SMTP_SENT_QUIT, SMTP_RECV_QUIT } smtp_state_t; #define SMTP_DEFAULT_HELO "smtpchecker.keepalived.org" /* Checker argument structure */ typedef struct _smtp_checker { /* non per host config data goes here */ const char *helo_name; /* data buffer */ char buff[SMTP_BUFF_MAX]; size_t buff_ctr; smtp_state_t state; } smtp_checker_t; /* macro utility */ #define FMT_SMTP_RS(H) (inet_sockaddrtopair (&(H)->dst)) /* Prototypes defs */ extern void install_smtp_check_keyword(void); #ifdef THREAD_DUMP extern void register_check_smtp_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/bfd_scheduler.h0000664000175000017500000000242314011447617016017 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: bfd_scheduler.c include file. * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #ifndef _BFD_SCHEDULER_H #define _BFD_SCHEDULER_H #include "scheduler.h" #include "bfd_data.h" extern void bfd_dispatcher_init(thread_ref_t); extern void bfd_dispatcher_release(bfd_data_t *); #ifdef THREAD_DUMP extern void register_bfd_scheduler_addresses(void); #endif #endif /* _BFD_SCHEDULER_H */ keepalived-2.3.3/keepalived/include/check_http.h0000664000175000017500000001074614661620303015345 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_http.c include file. * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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, */ #ifndef _CHECK_HTTP_H #define _CHECK_HTTP_H /* system includes */ #include #include #include #include #ifdef _WITH_REGEX_CHECK_ #define PCRE2_CODE_UNIT_WIDTH 8 #include #ifdef _WITH_REGEX_TIMERS_ #include #endif #endif #include /* local includes */ #include "scheduler.h" typedef enum { HTTP_PROTOCOL_1_0, HTTP_PROTOCOL_1_0C, HTTP_PROTOCOL_1_1, } http_protocol_t; #define HTTP_STATUS_CODE_MIN 100 #define HTTP_STATUS_CODE_MAX 599 #define HTTP_DEFAULT_STATUS_CODE_MIN 200 #define HTTP_DEFAULT_STATUS_CODE_MAX 299 /* Checker argument structure */ /* ssl specific thread arguments defs */ typedef struct _request { char *buffer; const char *extracted; int error; int status_code; size_t len; SSL *ssl; BIO *bio; EVP_MD_CTX *context; size_t content_len; size_t rx_bytes; #ifdef _WITH_REGEX_CHECK_ bool regex_matched; size_t start_offset; /* Offset into buffer to match from */ size_t regex_subject_offset; /* Offset into web page of start of buffer */ #ifdef _WITH_REGEX_TIMERS_ struct timespec req_time; unsigned num_match_calls; #endif #endif } request_t; #ifdef _WITH_REGEX_CHECK_ typedef struct _regex { const unsigned char *pattern; int pcre2_options; pcre2_code *pcre2_reCompiled; pcre2_match_data *pcre2_match_data; uint32_t pcre2_max_lookbehind; unsigned refcnt; #ifdef _WITH_REGEX_TIMERS_ struct timespec regex_time; unsigned num_match_calls; unsigned num_regex_urls; #endif /* Linked list member */ list_head_t e_list; } regex_t; #endif typedef struct _url { const char *path; const uint8_t *digest; unsigned long status_code[(HTTP_STATUS_CODE_MAX - HTTP_STATUS_CODE_MIN + 1 - 1) / (sizeof(unsigned long) * CHAR_BIT) + 1]; const char *virtualhost; ssize_t len_mismatch; bool tls_compliant; unsigned long last_ssl_error; #ifdef _WITH_REGEX_CHECK_ bool regex_no_match; regex_t *regex; size_t regex_min_offset; size_t regex_max_offset; /* One beyond max offset */ #ifndef PCRE2_DONT_USE_JIT bool regex_use_stack; #endif #endif /* Linked list member */ list_head_t e_list; } url_t; typedef struct _http_checker { unsigned proto; url_t *url_it; /* current url checked list entry */ url_t *failed_url; /* the url that is currently failing, if any */ request_t *req; /* GET buffer and SSL args */ list_head_t url; /* url_t */ http_protocol_t http_protocol; const char *virtualhost; #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ bool enable_sni; #endif bool fast_recovery; bool tls_compliant; int genhash_flags; } http_checker_t; #define GET_BUFFER_LENGTH 2048U #define MAX_BUFFER_LENGTH 4096U #define PROTO_HTTP 0x01 #define PROTO_SSL 0x02 #define GENHASH 0x01 #define GENHASH_VERBOSE 0x02 /* global defs */ #ifdef _REGEX_DEBUG_ extern bool do_regex_debug; #endif #ifdef _WITH_REGEX_TIMERS_ extern bool do_regex_timers; #endif /* Define prototypes */ extern void free_http_check(checker_t *); extern void install_http_check_keyword(void); extern void timeout_epilog(thread_ref_t, const char *); extern void dump_digest(unsigned char *, unsigned); extern void http_process_response(thread_ref_t, request_t *, size_t, url_t *); extern void http_handle_response(thread_ref_t, unsigned char digest[16], bool); extern void http_connect_thread(thread_ref_t); #ifdef THREAD_DUMP extern void register_check_http_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/check_ping.h0000664000175000017500000000247614661620303015324 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_udp.c include file. * * Author: Jie Liu, * * 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) 2019-2019 Alexandre Cassen, */ #ifndef _CHECK_PING_H #define _CHECK_PING_H #include "check_api.h" typedef struct _ping_check { uint16_t seq_no; bool response_expected; } ping_check_t; /* function prototypes */ extern bool set_ping_group_range(bool); extern void install_ping_check_keyword(void); #ifdef THREAD_DUMP extern void register_check_ping_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_notify.h0000664000175000017500000000275114134601064015605 /* * 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: vrrp_notify.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_NOTIFY_H #define _VRRP_NOTIFY_H /* local include */ #include "vrrp.h" extern void notify_instance_fifo(const vrrp_t *); extern void notify_group_fifo(const vrrp_sgroup_t *); extern void send_event_notify(vrrp_t *, int); extern void send_instance_notifies(vrrp_t *); extern void send_group_notifies(vrrp_sgroup_t *); extern void send_instance_priority_notifies(vrrp_t *); extern void notify_shutdown(void); #endif keepalived-2.3.3/keepalived/include/tracker.h0000664000175000017500000000413513666167373014700 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: tracker.h include file * * Author: Alexandre Cassen, * * 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-2020 Alexandre Cassen, */ #ifndef _TRACKER_H #define _TRACKER_H /* local includes */ #include "list_head.h" #ifdef _WITH_VRRP_ #include "vrrp.h" #endif #ifdef _WITH_LVS_ #include "check_api.h" #endif typedef enum { TRACK_VRRP = 0x01, TRACK_IF = 0x02, TRACK_SG = 0x04, TRACK_ADDR = 0x08, TRACK_ROUTE = 0x10, TRACK_RULE = 0x20, TRACK_SADDR = 0x40, TRACK_SROUTE = 0x80, TRACK_SRULE = 0x100, TRACK_VRRP_DYNAMIC = 0x200, TRACK_CHECKER = 0x400, } track_t; typedef union { void *obj; #ifdef _WITH_VRRP_ struct _vrrp_t *vrrp; /* vrrp instance */ #endif #ifdef _WITH_LVS_ checker_t *checker; /* checker instance */ #endif } tracking_obj_p; /* List structure from scripts, files and interfaces to tracking vrrp */ typedef struct _tracking_obj { int weight; /* Tracking weight, or zero for down instance */ int weight_multiplier; /* Which direction is weight applied */ tracking_obj_p obj; /* The object tracking this */ track_t type; /* Type of object being tracked */ /* linked list member */ list_head_t e_list; } tracking_obj_t; static inline void free_tracking_obj(tracking_obj_t *obj) { list_del_init(&obj->e_list); FREE(obj); } #endif keepalived-2.3.3/keepalived/include/check_file.h0000664000175000017500000000243113743553003015277 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_file.h include file * * Author: Quentin Armitage, * * 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) 2020-2020 Alexandre Cassen, */ #ifndef _CHECK_FILE_H #define _CHECK_FILE_H extern void install_file_check_keyword(void); extern void add_rs_to_track_files(void); extern void set_track_file_checkers_down(void); extern void set_track_file_weights(void); #ifdef THREAD_DUMP extern void register_check_file_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/check_daemon.h0000664000175000017500000000255313343356007015631 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_daemon.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _CHECK_DAEMON_H #define _CHECK_DAEMON_H /* system include */ #include /* Daemon define */ #define PROG_CHECK "Keepalived_healthcheckers" /* Global data */ extern bool using_ha_suspend; /* Prototypes */ extern int start_check_child(void); extern void check_validate_config(void); #ifdef THREAD_DUMP extern void register_check_parent_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/ipvswrapper.h0000664000175000017500000000655514613544726015631 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: ipvswrapper.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _IPVSWRAPPER_H #define _IPVSWRAPPER_H #include "config.h" #ifdef _WITH_VRRP_ #include "vrrp.h" #endif #include "libipvs.h" #include "check_data.h" #include "sockaddr.h" #define IPVS_ERROR 0 #define IPVS_SUCCESS 1 #define IPVS_STARTDAEMON IP_VS_SO_SET_STARTDAEMON #define IPVS_STOPDAEMON IP_VS_SO_SET_STOPDAEMON #define IPVS_FLUSH IP_VS_SO_SET_FLUSH #define IPVS_MASTER IP_VS_STATE_MASTER #define IPVS_BACKUP IP_VS_STATE_BACKUP #define IPVS_MASTER_BACKUP (IP_VS_STATE_MASTER | IP_VS_STATE_BACKUP) #define IPVS_DEF_SCHED "wlc" struct lvs_syncd_config { const char *ifname; /* handle LVS sync daemon state using this */ #ifdef _WITH_VRRP_ vrrp_t *vrrp; /* instance FSM & running on specific interface */ const char *vrrp_name; /* used during configuration and SNMP */ #endif unsigned syncid; /* 0 .. 255, or PARAMETER_UNSET if not configured */ #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ uint16_t sync_maxlen; sockaddr_t mcast_group; uint16_t mcast_port; uint8_t mcast_ttl; #endif bool daemon_set_reload; }; /* prototypes */ extern int ipvs_start(void); extern void ipvs_stop(void); extern void ipvs_set_timeouts(const ipvs_timeout_t *); extern void ipvs_flush_cmd(void); extern virtual_server_group_t *ipvs_get_group_by_name(const char *, list_head_t *) __attribute__ ((pure)); extern void ipvs_group_sync_entry(virtual_server_t *vs, virtual_server_group_entry_t *vsge); extern void ipvs_group_remove_entry(virtual_server_t *, virtual_server_group_entry_t *); extern void unset_vsge_alive(virtual_server_group_entry_t *, const virtual_server_t *); extern int ipvs_cmd(int, virtual_server_t *, real_server_t *); extern bool ipvs_syncd_changed(const struct lvs_syncd_config *, const struct lvs_syncd_config *) __attribute__((pure)); extern void ipvs_syncd_cmd(int, const struct lvs_syncd_config *, int, bool); #ifdef _WITH_VRRP_ extern void ipvs_syncd_master(const struct lvs_syncd_config *); extern void ipvs_syncd_backup(const struct lvs_syncd_config *); #endif #ifdef _WITH_NFTABLES_ extern void remove_fwmark_vs(virtual_server_t *, int); extern void add_fwmark_vs(virtual_server_t *, int); #endif /* Refresh VS statistics at most every global_data->snmp_vs_stats_update_interval */ extern void ipvs_vs_update_stats(virtual_server_t * vs); /* Refresh RS statistics at most every global_data->snmp_rs_stats_update_interval */ extern void ipvs_rs_update_stats(virtual_server_t * vs); #endif keepalived-2.3.3/keepalived/include/ip_vs.h0000664000175000017500000000641714613546754014367 /* * IP Virtual Server * data structure and functionality definitions */ #ifndef _KEEPALIVED_IP_VS_H #define _KEEPALIVED_IP_VS_H #include "config.h" /* System includes */ #include /* Force inclusion of net/if.h before linux/if.h */ #include #include #include /* Prior to Linux 4.2 have to include linux/in.h and linux/in6.h * or linux/netlink.h to include linux/netfilter.h */ #include /* For nf_inet_addr */ #include /* The kernel's valid values for a real server weight are 0..INT32_MAX * We reserve +/- INT32_MAX for fault state. */ #define IPVS_WEIGHT_MAX INT32_MAX #define IPVS_WEIGHT_LIMIT (IPVS_WEIGHT_MAX) #define IPVS_WEIGHT_FAULT (-IPVS_WEIGHT_MAX - 1) #define IPVS_FWMARK_MAX UINT32_MAX #ifdef _WITH_LVS_64BIT_STATS_ struct ip_vs_stats64 { __u64 conns; /* connections scheduled */ __u64 inpkts; /* incoming packets */ __u64 outpkts; /* outgoing packets */ __u64 inbytes; /* incoming bytes */ __u64 outbytes; /* outgoing bytes */ __u64 cps; /* current connection rate */ __u64 inpps; /* current in packet rate */ __u64 outpps; /* current out packet rate */ __u64 inbps; /* current in byte rate */ __u64 outbps; /* current out byte rate */ }; typedef struct ip_vs_stats64 ip_vs_stats_t; #define ip_vs_stats stats #else typedef struct ip_vs_stats_user ip_vs_stats_t; #define ip_vs_stats user.stats #endif struct ip_vs_service_app { struct ip_vs_service_user user; uint16_t af; union nf_inet_addr nf_addr; char pe_name[IP_VS_PENAME_MAXLEN + 1]; }; struct ip_vs_dest_app { struct ip_vs_dest_user user; uint16_t af; union nf_inet_addr nf_addr; #ifdef _HAVE_IPVS_TUN_TYPE_ int tun_type; int tun_port; #ifdef _HAVE_IPVS_TUN_CSUM_ int tun_flags; #endif #endif }; struct ip_vs_service_entry_app { struct ip_vs_service_entry user; #ifdef _WITH_LVS_64BIT_STATS_ ip_vs_stats_t stats; #endif uint16_t af; union nf_inet_addr nf_addr; char pe_name[IP_VS_PENAME_MAXLEN + 1]; }; struct ip_vs_dest_entry_app { struct ip_vs_dest_entry user; #ifdef _WITH_LVS_64BIT_STATS_ ip_vs_stats_t stats; #endif uint16_t af; union nf_inet_addr nf_addr; }; struct ip_vs_get_dests_app { uint16_t af; /* Needed if don't get IPVS_DEST_ATTR_ADDR_FAMILY */ unsigned num_entries; /* Number of entries space allocated for */ struct ip_vs_get_dests_entries_app { /* number of real servers */ unsigned int num_dests; /* the real servers */ struct ip_vs_dest_entry_app entrytable[]; } user; }; /* The argument to IP_VS_SO_GET_SERVICES */ struct ip_vs_get_services_app { struct { /* number of virtual services */ unsigned int num_services; /* service table */ struct ip_vs_service_entry_app entrytable[]; } user; }; /* Make sure we don't have an inconsistent definition */ #if IP_VS_IFNAME_MAXLEN > IFNAMSIZ #error The code assumes that IP_VS_IFNAME_MAXLEN <= IFNAMSIZ #endif struct ip_vs_daemon_app { struct ip_vs_daemon_user user; #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ /* UDP Payload Size */ uint16_t sync_maxlen; /* Multicast Port (base) */ uint16_t mcast_port; /* Multicast TTL */ uint8_t mcast_ttl; /* Multicast Address Family */ uint16_t mcast_af; /* Multicast Address */ union nf_inet_addr mcast_group; #endif }; #endif /* _KEEPALIVED_IP_VS_H */ keepalived-2.3.3/keepalived/include/check_nftables.h0000664000175000017500000000317214115724516016164 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_nftables.c include file. * * Author: Quentin Armitage, * * 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) 2020-2020 Alexandre Cassen, */ #ifndef _CHECK_NFTABLES_H #define _CHECK_NFTABLES_H #include "config.h" #include "nftables.h" #include "check_data.h" #include "sockaddr.h" #define DEFAULT_NFTABLES_IPVS_TABLE "keepalived_ipvs" #define DEFAULT_IPVS_NF_START_FWMARK 1000 #ifdef _INCLUDE_UNUSED_CODE_ extern void nft_add_ipvs_entry(const sockaddr_t *, uint16_t, uint32_t); extern void nft_remove_ipvs_entry(const sockaddr_t *, uint16_t, uint32_t); #endif extern void nft_ipvs_end(void); extern unsigned set_vs_fwmark(virtual_server_t *); extern void clear_vs_fwmark(virtual_server_t *); extern void remove_vs_fwmark_entry(virtual_server_t *, virtual_server_group_entry_t *); #endif keepalived-2.3.3/keepalived/include/vrrp_dbus.h0000664000175000017500000000266114500030210015214 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_dbus.c include file. * * Author: Alexandre Cassen, * * 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) 2016-2017 Alexandre Cassen, */ #ifndef _VRRP_DBUS_H #define _VRRP_DBUS_H /* System includes */ #include /* Local includes */ #include "vrrp.h" #include "list_head.h" extern const char *dbus_no_interface_name; void dbus_send_state_signal(vrrp_t *); void dbus_remove_object(const vrrp_t *); void dbus_reload(const list_head_t *, const list_head_t *); bool dbus_start(void); void dbus_stop(void); #ifdef THREAD_DUMP extern void register_vrrp_dbus_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/ipwrapper.h0000664000175000017500000000404614661620303015236 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: ipwrapper.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _IPWRAPPER_H #define _IPWRAPPER_H /* system includes */ #include #include /* local includes */ #include "check_data.h" #include "check_api.h" #include "utils.h" /* UP & DOWN value */ #define UP true #define DOWN false /* LVS command set by kernel */ #define LVS_CMD_ADD IP_VS_SO_SET_ADD #define LVS_CMD_DEL IP_VS_SO_SET_DEL #define LVS_CMD_ADD_DEST IP_VS_SO_SET_ADDDEST #define LVS_CMD_DEL_DEST IP_VS_SO_SET_DELDEST #define LVS_CMD_EDIT_DEST IP_VS_SO_SET_EDITDEST static inline bool __attribute((pure)) rs_iseq(const real_server_t *rs_a, const real_server_t *rs_b) { return sockstorage_equal(&rs_a->addr, &rs_b->addr); } /* prototypes */ extern void update_svr_wgt(int64_t, virtual_server_t *, real_server_t *, bool); extern void set_checker_state(checker_t *, bool); extern void update_svr_checker_state(bool, checker_t *); extern bool init_services(void); extern void clear_services(void); extern void set_quorum_states(void); extern void clear_diff_services(void); extern void check_new_rs_state(void); extern void link_vsg_to_vs(void); #endif keepalived-2.3.3/keepalived/include/bfd_parser.h0000664000175000017500000000225214011447617015335 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: bfd_parser.c include file. * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #ifndef _BFD_PARSER_H #define _BFD_PARSER_H #include #include "vector.h" extern void init_bfd_keywords(bool); extern const vector_t *bfd_init_keywords(void); #endif /* _BFD_PARSER_H */ keepalived-2.3.3/keepalived/include/vrrp_static_track.h0000664000175000017500000000350513667214205016755 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_static_track.c include file. * * Author: Quentin Armitage, * * 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) 2018-2018 Alexandre Cassen, */ #ifndef _VRRP_STATIC_TRACK_H #define _VRRP_STATIC_TRACK_H /* global includes */ #include /* local includes */ #include "vector.h" #include "vrrp_if.h" /* Parameters for static track groups */ typedef struct _static_track_group { const char *gname; /* Group name */ const vector_t *iname; /* Set of VRRP instances in this group, only used during initialisation */ list_head_t vrrp_instances; /* tracking_obj_t - List of VRRP instances */ /* linked list member */ list_head_t e_list; } static_track_group_t; extern void free_static_track_group(static_track_group_t *); extern void dump_static_track_group(FILE *, const static_track_group_t *); extern static_track_group_t *static_track_group_find(const char *); extern void static_track_group_init(void); extern void static_track_group_reinstate_config(interface_t *); #endif keepalived-2.3.3/keepalived/include/global_parser.h0000664000175000017500000000223413442662177016051 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_parser.c include file. * * Author: Alexandre Cassen, * * 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-2016 Alexandre Cassen, */ #ifndef _GLOBAL_PARSER_H #define _GLOBAL_PARSER_H #include #include #include "vector.h" /* Prototypes */ extern void init_global_keywords(bool); #endif keepalived-2.3.3/keepalived/include/vrrp_iptables_calls.h0000664000175000017500000000532413551175725017271 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_iptables_calls.c include file. * * Author: Quentin Armitage, * * 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, */ #ifndef _VRRP_IPTABLES_CALLS_H #define _VRRP_IPTABLES_CALLS_H #include "config.h" #include #include #include #include #include "vrrp_ipaddress.h" #define APPEND_RULE UINT_MAX extern struct iptc_handle* ip4tables_open ( const char*); extern int ip4tables_close ( struct iptc_handle*, int); extern int ip4tables_is_chain(struct iptc_handle*, const char*); extern int ip4tables_process_entry( struct iptc_handle* handle, const char* chain_name, unsigned int rulenum, const char* target_name, const ip_address_t* src_ip_address, const ip_address_t* dst_ip_address, const char* in_iface, const char* out_iface, uint16_t protocol, uint8_t type, int cmd, uint8_t flags, bool force); extern struct ip6tc_handle* ip6tables_open ( const char* tablename ); extern int ip6tables_close ( struct ip6tc_handle* handle, int updated ); extern int ip6tables_is_chain(struct ip6tc_handle* handle, const char* chain_name); extern int ip6tables_process_entry( struct ip6tc_handle* handle, const char* chain_name, unsigned int rulenum, const char* target_name, const ip_address_t* src_ip_address, const ip_address_t* dst_ip_address, const char* in_iface, const char* out_iface, uint16_t protocol, uint8_t type, int cmd, uint8_t flags, bool force); extern int ip4tables_add_rules(struct iptc_handle *, const char *, unsigned int, uint8_t, uint8_t, const char *, const ip_address_t *, const ip_address_t *, const char *, uint16_t, uint8_t, int, bool); extern int ip6tables_add_rules(struct ip6tc_handle *, const char *, unsigned int, uint8_t, uint8_t, const char *, const ip_address_t *, const ip_address_t *, const char *, uint16_t, uint8_t, int, bool); #ifdef _LIBIPTC_DYNAMIC_ extern bool iptables_lib_init(uint8_t); #endif #endif keepalived-2.3.3/keepalived/include/check_bfd.h0000664000175000017500000000447614661620303015124 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_bfd.c include file. * * Author: Quentin Armitage * * 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, */ #ifndef _CHECK_BFD_H #define _CHECK_BFD_H #include "scheduler.h" /* external bfd we read to track forwarding to remote systems */ typedef struct _checker_tracked_bfd { char *bname; /* bfd name */ // int weight; /* Default weight */ list_head_t tracking_rs; /* tracking_obj_t */ /* Linked list member */ list_head_t e_list; } checker_tracked_bfd_t; /* Checker Reference Tracked bfd structure definition. * This is a duplication of bfd_checker_t but here for * readability. */ typedef struct _cref_tracked_bfd { checker_tracked_bfd_t *bfd; /* track bfd pointer, cannot be NULL */ // int weight; /* Weight for bfd */ /* Linked list member */ list_head_t e_list; } cref_tracked_bfd_t; /* bfd_checker structure */ typedef struct _bfd_checker { checker_tracked_bfd_t *bfd; /* track bfd pointer, cannot be NULL */ // int weight; // Set in bfd_weight_handler /* Linked list member */ list_head_t e_list; } bfd_checker_t; /* Prototypes defs */ extern void free_checker_tracked_bfd_list(list_head_t *); extern void free_bfds_rs_list(list_head_t *); extern void dump_bfds_rs_list(FILE *, const list_head_t *); extern void install_bfd_check_keyword(void); extern void start_bfd_monitoring(thread_master_t *); extern void checker_bfd_dispatcher_release(void); #ifdef THREAD_DUMP extern void register_check_bfd_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_if.h0000664000175000017500000002457614705540346014715 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_if.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_IF_H #define _VRRP_IF_H #include "config.h" /* global includes */ #include #include #include #include #include #include #include #ifdef _HAVE_NET_LINUX_IF_H_COLLISION_ #define _LINUX_IF_H #endif #include #ifdef _HAVE_VRRP_VMAC_ #include #endif /* local includes */ #include "scheduler.h" #include "list_head.h" #include "timer.h" #include "sockaddr.h" #define LINK_UP 1 #define LINK_DOWN 0 #define POLLING_DELAY TIMER_HZ #ifdef _WITH_LINKBEAT_ /* Interface Linkbeat code selection */ #define LB_IOCTL 0x1 #define LB_MII 0x2 #define LB_ETHTOOL 0x4 #endif /* We need a default MTU in case a vrrp instance using unicast doesn't specify an interface */ #define DEFAULT_MTU 1500 /* I don't know what the correct type is. * The kernel has ifindex in the range [1, INT_MAX], but IFLA_LINK is defined * to be __u32. See dev_new_index() in net/core/dev.c and net/core/rtnetlink.c. * ifaddrmsg.ifa_index (See /usr/include/linux/if_addr.h> is __u32. * /usr/include/linux/rtnetlink.h has them as ints. * RTA_OIF and RTA_IIF are u32. * RFC2553 defines sin6_scopeid to be a uint32_t, and it can hold an ifindex */ typedef uint32_t ifindex_t; /* Structure for delayed sending of gratuitous ARP/NA messages */ typedef struct _garp_delay { timeval_t garp_interval; /* Delay between sending gratuitous ARP messages on an interface */ bool have_garp_interval; /* True if delay */ timeval_t gna_interval; /* Delay between sending gratuitous NA messages on an interface */ bool have_gna_interval; /* True if delay */ timeval_t garp_next_time; /* Time when next gratuitous ARP message can be sent */ timeval_t gna_next_time; /* Time when next gratuitous NA message can be sent */ /* linked list of ip_address_t that have GARP/NAs pending */ list_head_t garp_list; list_head_t gna_list; /* linked list member of garp_delay_t */ list_head_t e_list; } garp_delay_t; typedef struct _sin_addr { union { struct in_addr sin_addr; /* IPv4 address */ struct in6_addr sin6_addr; /* IPv6 address */ } u; /* linked list member */ list_head_t e_list; } sin_addr_t; #ifdef _HAVE_VRRP_VMAC_ typedef enum { IF_TYPE_STANDARD, IF_TYPE_MACVLAN, #ifdef _HAVE_VRRP_IPVLAN_ IF_TYPE_IPVLAN, #endif #ifdef _HAVE_VRF_ IF_TYPE_VRF, #endif } if_type_t; #ifdef _HAVE_VRRP_IPVLAN_ #define IS_MAC_IP_VLAN(IFP) ((IFP)->if_type == IF_TYPE_MACVLAN || (IFP)->if_type == IF_TYPE_IPVLAN) #else #define IS_MAC_IP_VLAN(IFP) ((IFP)->if_type == IF_TYPE_MACVLAN) #endif #endif /* defines MAX_ADDR_LEN as 7, whereas defines it as 32. * Make sure we have the right definition. */ #if MAX_ADDR_LEN == 7 #error "MAX_ADDR_LEN == 7 - probably was included after " #elif MAX_ADDR_LEN != 32 #error "MAX_ADDR_LEN != 32 - probably was included after " #endif /* Interface structure definition */ typedef struct _interface { char ifname[IFNAMSIZ]; /* Interface name */ ifindex_t ifindex; /* Interface index */ #ifdef _WITH_VRRP_ struct in_addr sin_addr; /* IPv4 primary IPv4 address */ struct in6_addr sin6_addr; /* IPv6 primary link local address */ list_head_t sin_addr_l; /* List of extra IPv4 interface addresses - sin_addr_t */ list_head_t sin6_addr_l; /* List of extra IPv6 interface addresses - sin_addr_t */ #endif unsigned ifi_flags; /* Kernel flags */ bool seen_up; /* True once we have first seen the interface up */ thread_ref_t flags_change_thread; unsigned up_debounce_timer; unsigned down_debounce_timer; uint32_t mtu; /* MTU for this interface_t */ unsigned short hw_type; /* Type of hardware address */ u_char hw_addr[MAX_ADDR_LEN]; /* MAC address */ u_char hw_addr_bcast[MAX_ADDR_LEN]; /* broadcast address */ size_t hw_addr_len; /* MAC addresss length */ #ifdef _WITH_LINKBEAT_ bool linkbeat_use_polling; /* Poll the interface for status, rather than use netlink */ int lb_type; /* Interface regs selection */ #endif #ifdef _HAVE_VRRP_VMAC_ if_type_t if_type; /* interface type */ int vmac_type; /* Type of macvlan or ipvlan */ #ifdef HAVE_DECL_IFLA_IPVLAN_FLAGS int ipvlan_flags; /* bridge/private/vepa */ #endif ifindex_t base_ifindex; /* Only used at startup if we find vmac i/f before base i/f */ #ifdef HAVE_IFLA_LINK_NETNSID int base_netns_id; /* Network namespace of the parent interface */ #endif struct _interface *base_ifp; /* Base interface (if interface is a VMAC interface), otherwise the physical interface */ bool is_ours; /* keepalived created the interface */ bool deleting; /* Set when we are deleting the interface */ bool seen_interface; /* The interface has existed at some point since we started */ bool changeable_type; /* The interface type or underlying interface can be changed */ #ifdef _HAVE_VRF_ ifindex_t vrf_master_ifindex; /* Only used at startup if we find i/f before master i/f */ struct _interface *vrf_master_ifp; /* VRF master interface - pointer to self if VRF master */ uint32_t vrf_tb_id; /* Table id of VRF */ #endif int reset_arp_config; /* Count of how many vrrps have changed arp parameters on interface */ bool arp_ignore; /* Original value of arp_ignore to be restored */ bool arp_filter; /* Original value of arp_filter to be restored */ unsigned rp_filter; /* < UINT_MAX if we have changed the value */ uint32_t group; /* GROUP for this interface_t */ #endif garp_delay_t *garp_delay; /* Delays for sending gratuitous ARP/NA */ timeval_t last_gna_router_check; /* Time we last checked if IPv6 forwarding set on interface */ bool gna_router; /* Router flag for NA messages */ bool promote_secondaries; /* Original value of promote_secondaries to be restored */ uint32_t reset_promote_secondaries; /* Count of how many vrrps have changed promote_secondaries on interface */ list_head_t tracking_vrrp; /* tracking_obj_t - vrrp instances tracking this interface */ /* linked list member */ list_head_t e_list; } interface_t; /* Tracked interface structure definition */ typedef struct _tracked_if { int weight; /* tracking weight when non-zero */ bool weight_reverse; /* which direction is the weight applied */ interface_t *ifp; /* interface backpointer, cannot be NULL */ /* linked list member */ list_head_t e_list; } tracked_if_t; /* Macros */ #define IF_NAME(X) ((X)->ifname) #define IF_INDEX(X) ((X)->ifindex) #ifdef _HAVE_VRRP_VMAC_ #define IF_BASE_INDEX(X) ((X)->base_ifp->ifindex) #define IF_BASE_IFP(X) ((X)->base_ifp) #else #define IF_BASE_INDEX(X) ((X)->ifindex) #define IF_BASE_IFP(X) (X) #endif #define IF_ADDR(X) ((X)->sin_addr.s_addr) #define IF_ADDR6(X) ((X)->sin6_addr) #define IF_HWADDR(X) ((X)->hw_addr) #ifdef _WITH_LINKBEAT_ #define IF_MII_SUPPORTED(X) ((X)->lb_type & LB_MII) #define IF_ETHTOOL_SUPPORTED(X) ((X)->lb_type & LB_ETHTOOL) #endif #define FLAGS_UP(X) (((X) & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) #define IF_FLAGS_UP(X) (FLAGS_UP((X)->ifi_flags)) #ifdef _HAVE_VRRP_VMAC_ #define IF_ISUP(X) (IF_FLAGS_UP(X) && (!(X)->vmac_type || IF_FLAGS_UP((X)->base_ifp))) #else #define IF_ISUP(X) (IF_FLAGS_UP(X)) #endif typedef enum if_lookup { IF_NO_CREATE, IF_CREATE_IF_DYNAMIC, IF_CREATE_ALWAYS, IF_CREATE_NETLINK, IF_CREATE_NOT_EXIST, } if_lookup_t; /* Global data */ extern list_head_t garp_delay; /* prototypes */ extern interface_t *if_get_by_ifindex(ifindex_t) __attribute__ ((pure)); #ifdef _HAVE_VRRP_VMAC_ extern interface_t * if_get_by_vmac(uint8_t, int, const interface_t *, const u_char hw_addr[ETH_ALEN]) __attribute__ ((pure)); #endif extern interface_t *get_default_if(void); extern interface_t *if_get_by_ifname(const char *, if_lookup_t); extern sin_addr_t *if_extra_ipaddress_alloc(interface_t *, void *, unsigned char); extern void if_extra_ipaddress_free(sin_addr_t *); extern void if_extra_ipaddress_free_list(list_head_t *); extern void dump_garp_delay_list(FILE *, list_head_t *); extern void free_garp_delay(garp_delay_t *); extern garp_delay_t *alloc_garp_delay(void); extern void set_default_garp_delay(void); extern void init_interface_queue(void); #ifdef _WITH_LINKBEAT_ extern void init_interface_linkbeat(void); extern void close_interface_linkbeat(void); #endif extern list_head_t *get_interface_queue(void) __attribute__ ((const)); extern void free_interface_queue(void); extern void free_old_interface_queue(void); extern void dump_interface_queue(FILE *, list_head_t *); extern void reset_interface_queue(void); extern int if_join_vrrp_group(sa_family_t, int *, const interface_t *, const sockaddr_t *); extern int if_setsockopt_bindtodevice(int *, const interface_t *); extern int if_setsockopt_hdrincl(int *); extern int if_setsockopt_ipv6_checksum(int *); extern int if_setsockopt_mcast_all(sa_family_t, int *); extern int if_setsockopt_mcast_loop(sa_family_t, int *); extern int if_setsockopt_mcast_hops(sa_family_t, int *); extern int if_setsockopt_mcast_if(sa_family_t, int *, const interface_t *); extern int if_setsockopt_priority(int *, int); extern int if_setsockopt_rcvbuf(int *, int); extern int if_setsockopt_no_receive(int *); extern void interface_up(interface_t *); extern void interface_down(interface_t *); extern void cleanup_lost_interface(interface_t *); extern void recreate_vmac_thread(thread_ref_t); void update_mtu(interface_t *); extern void update_added_interface(interface_t *); #ifdef THREAD_DUMP extern void register_vrrp_if_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/bfd_event.h0000664000175000017500000000231614011447617015163 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: bfd_event.c include file. * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #ifndef _BFD_EVENT_H #define _BFD_EVENT_H #include "bfd.h" typedef struct _bfd_event { char iname[BFD_INAME_MAX]; uint8_t state; timeval_t sent_time; } bfd_event_t; extern void bfd_event_send(bfd_t *); #endif /* _BFD_EVENT_H */ keepalived-2.3.3/keepalived/include/track_process.h0000664000175000017500000000314713662036701016073 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: track_process.c include file. * * Author: Alexandre Cassen, * * 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) 2018-2018 Alexandre Cassen, */ #ifndef _VRRP_TRACK_PROCESS_H #define _VRRP_TRACK_PROCESS_H /* global includes */ #include /* local includes */ #include "list_head.h" #include "vrrp.h" #ifdef _TRACK_PROCESS_DEBUG_ extern bool do_track_process_debug; extern bool do_track_process_debug_detail; #endif extern bool proc_events_not_supported; /* prototypes */ extern void reload_track_processes(void); extern bool open_track_processes(void); extern bool close_track_processes(void); extern bool init_track_processes(list_head_t *); extern void end_process_monitor(void); #ifdef THREAD_DUMP extern void register_process_monitor_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/check_snmp.h0000664000175000017500000000262313300220353015324 /* * 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: check_snmp.c program include file. * * Author: Vincent Bernat * * 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, */ #ifndef _CHECK_SNMP_H #define _CHECK_SNMP_H #include #include "check_data.h" /* Prototypes */ extern void check_snmp_agent_init(const char *); extern void check_snmp_agent_close(void); extern void check_snmp_rs_trap(real_server_t *, virtual_server_t *, bool); extern void check_snmp_quorum_trap(virtual_server_t *, bool); #endif keepalived-2.3.3/keepalived/include/vrrp_ip_rule_route_parser.h0000664000175000017500000000350314227044032020521 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: iprule and iproute parser * * Author: Quentin Armitage, * * 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) 2016 Quentin Armitage, * Copyright (C) 2016-2017 Alexandre Cassen, */ #ifndef _VRRP_IP_RULE_ROUTE_PARSER_H #define _VRRP_IP_RULE_ROUTE_PARSER_H #include #include #if HAVE_DECL_RTA_ENCAP && HAVE_DECL_LWTUNNEL_ENCAP_MPLS #include "vrrp_iproute.h" #endif extern bool get_realms(uint32_t *, const char *); extern bool get_u8(uint8_t *, const char *, uint8_t, const char*); extern bool get_u32(uint32_t *, const char *, uint32_t, const char*); extern bool get_u16(uint16_t *, const char *, uint16_t, const char*); extern bool get_u64(uint64_t *, const char *, uint64_t, const char*); extern bool get_time_rtt(uint32_t *, const char *, unsigned, const char *); extern bool get_addr64(uint64_t *, const char *); #if HAVE_DECL_RTA_ENCAP && HAVE_DECL_LWTUNNEL_ENCAP_MPLS extern bool parse_mpls_address(const char *, encap_mpls_t *); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_daemon.h0000664000175000017500000000267113760514000015536 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_daemon.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_DAEMON_H #define _VRRP_DAEMON_H #include /* Daemon define */ #define PROG_VRRP "Keepalived_vrrp" extern bool non_existent_interface_specified; extern const char * const igmp_link_local_mcast_reports; #ifdef _VRRP_FD_DEBUG_ extern bool do_vrrp_fd_debug; #endif /* Prototypes */ extern int start_vrrp_child(void); extern void vrrp_validate_config(void); #ifdef THREAD_DUMP extern void register_vrrp_parent_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/check_dns.h0000664000175000017500000000576214011447617015160 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_dns.c include file. * * Author: Masanobu Yasui, * Masaya Yamamoto, * * 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) 2016 KLab Inc. * Copyright (C) 2016-2017 Alexandre Cassen, */ #ifndef _CHECK_DNS_H #define _CHECK_DNS_H #include #include #define DNS_DEFAULT_RETRY 3 #define DNS_DEFAULT_TYPE DNS_TYPE_SOA #define DNS_DEFAULT_NAME "" #define DNS_BUFFER_SIZE 768 #define DNS_QR(flags) ((flags >> 15) & 0x0001) /* UNUSED #define DNS_OP(flags) ((flags >> 11) & 0x000F) #define DNS_AA(flags) ((flags >> 10) & 0x0001) #define DNS_TC(flags) ((flags >> 9) & 0x0001) #define DNS_RD(flags) ((flags >> 8) & 0x0001) #define DNS_RA(flags) ((flags >> 7) & 0x0001) #define DNS_Z(flags) ((flags >> 4) & 0x0007) */ #define DNS_RC(flags) ((flags >> 0) & 0x000F) /* UNUSED #define DNS_SET_QR(flags, val) (flags |= ((val & 0x0001) << 15)) #define DNS_SET_OP(flags, val) (flags |= ((val & 0x000F) << 11)) #define DNS_SET_AA(flags, val) (flags |= ((val & 0x0001) << 10)) #define DNS_SET_TC(flags, val) (flags |= ((val & 0x0001) << 9)) */ #define DNS_SET_RD(flags, val) (flags |= ((val & 0x0001) << 8)) /* UNUSED #define DNS_SET_RA(flags, val) (flags |= ((val & 0x0001) << 7)) #define DNS_SET_Z(flags, val) (flags |= ((val & 0x0007) << 4)) #define DNS_SET_RC(flags, val) (flags |= ((val & 0x000F) << 0)) */ #define DNS_TYPE_A 1 #define DNS_TYPE_NS 2 #define DNS_TYPE_CNAME 5 #define DNS_TYPE_SOA 6 #define DNS_TYPE_MX 15 #define DNS_TYPE_TXT 16 #define DNS_TYPE_AAAA 28 #define DNS_TYPE_RRSIG 46 #define DNS_TYPE_DNSKEY 48 typedef struct _dns_type { uint16_t type; const char * const label; } dns_type_t; extern const dns_type_t DNS_TYPE[]; typedef struct _dns_header { uint16_t id; uint16_t flags; uint16_t qdcount; uint16_t ancount; uint16_t nscount; uint16_t arcount; } dns_header_t; typedef struct _dns_check { uint16_t type; const char *name; uint8_t sbuf[DNS_BUFFER_SIZE] __attribute__((aligned(__alignof__(dns_header_t)))); size_t slen; } dns_check_t; extern void install_dns_check_keyword(void); #ifdef THREAD_DUMP extern void register_check_dns_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/vrrp_scheduler.h0000664000175000017500000000506314770002126016252 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vrrp_scheduler.c include file. * * Author: Alexandre Cassen, * * 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, */ #ifndef _VRRP_SCHEDULER_H #define _VRRP_SCHEDULER_H /* system include */ #include #include #include /* local includes */ #include "scheduler.h" #include "timer.h" #include "vrrp_track.h" #include "vrrp_data.h" #include "vrrp.h" /* global vars */ extern bool vrrp_initialised; extern timeval_t vrrp_delayed_start_time; /* VRRP TSM Macro */ #define VRRP_TSM_HANDLE(S,V) \ do { \ if ((V)->sync && \ (*(VRRP_TSM[S][(V)->state].handler))) \ (*(VRRP_TSM[S][(V)->state].handler)) (V); \ } while (0) #ifdef _TSM_DEBUG_ extern bool do_tsm_debug; #endif #ifdef _RECVMSG_DEBUG_ extern bool do_recvmsg_debug; extern bool do_recvmsg_debug_dump; #endif /* extern prototypes */ extern void vrrp_init_instance_sands(vrrp_t *); extern void vrrp_thread_requeue_read(vrrp_t *); extern void vrrp_thread_add_read(vrrp_t *); extern void vrrp_dispatcher_init(thread_ref_t); #ifdef _WITH_BFD_ extern void cancel_vrrp_threads(void); #endif extern void vrrp_dispatcher_release(vrrp_data_t *); extern void vrrp_gratuitous_arp_thread(thread_ref_t); extern void vrrp_lower_prio_gratuitous_arp_thread(thread_ref_t); extern void vrrp_gratuitous_arp_refresh_thread(thread_ref_t); #ifdef _HAVE_VRRP_VMAC_ extern void vrrp_gratuitous_arp_vmac_update_thread(thread_ref_t); #endif extern void vrrp_arp_thread(thread_ref_t); extern void vrrp_gna_thread(thread_ref_t); extern void try_up_instance(vrrp_t *, bool, vrrp_fault_fl_t); #ifdef _WITH_DUMP_THREADS_ extern void dump_threads(void); #endif #ifdef THREAD_DUMP extern void register_vrrp_scheduler_addresses(void); #endif #endif keepalived-2.3.3/keepalived/include/check_data.h0000664000175000017500000002413314706175715015306 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Healthcheckers dynamic data structure definition. * * Author: Alexandre Cassen, * * 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, */ #ifndef _CHECK_DATA_H #define _CHECK_DATA_H #include "config.h" /* system includes */ #include #include #include #include #include #include /* local includes */ #include "logger.h" #include "ip_vs.h" #include "list_head.h" #include "vector.h" #include "notify.h" #include "utils.h" #ifdef _WITH_BFD_ #include "check_bfd.h" #endif #ifdef _WITH_NFTABLES_ #include "logger.h" #include "sockaddr.h" #endif /* Daemon dynamic data structure definition */ #define KEEPALIVED_DEFAULT_DELAY (60 * TIMER_HZ) #ifdef _WITH_NFTABLES_ /* Used for arrays of protocol entries */ typedef enum { TCP_INDEX, UDP_INDEX, SCTP_INDEX, PROTO_INDEX_NONE, PROTO_INDEX_MAX = PROTO_INDEX_NONE } proto_index_t; #endif /* SSL specific data */ typedef struct _ssl_data { int enable; int strong_check; SSL_CTX *ctx; const SSL_METHOD *meth; const char *password; const char *cafile; const char *certfile; const char *keyfile; } ssl_data_t; /* Real Server definition */ typedef struct _real_server { sockaddr_t addr; int64_t effective_weight; int64_t peffective_weight; /* previous weight * used for reloading */ int iweight; /* Initial weight */ unsigned forwarding_method; /* NAT/TUN/DR */ #ifdef _HAVE_IPVS_TUN_TYPE_ int tun_type; /* tunnel type */ unsigned tun_port; /* tunnel port for gue tunnels */ #ifdef _HAVE_IPVS_TUN_CSUM_ int tun_flags; /* tunnel checksum type for gue/gre tunnels */ #endif #endif #ifdef _WITH_SNMP_CHECKER_ const char *snmp_name; #endif uint32_t u_threshold; /* Upper connection limit. */ uint32_t l_threshold; /* Lower connection limit. */ int inhibit; /* Set weight to 0 instead of removing * the service from IPVS topology. */ notify_script_t *notify_up; /* Script to launch when RS is added to LVS */ notify_script_t *notify_down; /* Script to launch when RS is removed from LVS */ int alpha; /* true if alpha mode is default. */ unsigned int connection_to; /* connection time-out */ unsigned long delay_loop; /* Interval between running checker */ unsigned long warmup; /* max random timeout to start checker */ unsigned retry; /* number of retries before failing */ unsigned long delay_before_retry; /* interval between retries */ int smtp_alert; /* Send email on status change */ bool alive; unsigned num_failed_checkers;/* Number of failed checkers */ bool set; /* in the IPVS table */ bool reloaded; /* active state was copied from old config while reloading */ const char *virtualhost; /* Default virtualhost for HTTP and SSL health checkers */ #if defined(_WITH_SNMP_CHECKER_) /* Statistics */ uint32_t activeconns; /* active connections */ uint32_t inactconns; /* inactive connections */ uint32_t persistconns; /* persistent connections */ #ifndef _WITH_LVS_64BIT_STATS_ struct ip_vs_stats_user stats; #else struct ip_vs_stats64 stats; #endif #endif list_head_t track_files; /* tracked_file_monitor_t - Files whose value we monitor */ #ifdef _WITH_BFD_ list_head_t tracked_bfds; /* cref_tracked_bfd_t */ #endif /* Linked list member */ list_head_t e_list; list_head_t checkers_list; } real_server_t; /* Virtual Server group definition */ typedef struct _virtual_server_group_entry { bool is_fwmark; union { struct { sockaddr_t addr; sockaddr_t addr_end; unsigned tcp_alive; unsigned udp_alive; unsigned sctp_alive; }; struct { uint32_t vfwmark; uint16_t fwm_family; unsigned fwm4_alive; unsigned fwm6_alive; }; }; bool reloaded; /* Linked list member */ list_head_t e_list; } virtual_server_group_entry_t; typedef struct _virtual_server_group { char *gname; list_head_t addr_range; list_head_t vfwmark; bool have_ipv4; bool have_ipv6; bool fwmark_no_family; #ifdef _WITH_NFTABLES_ unsigned auto_fwmark[PROTO_INDEX_MAX]; #endif /* Linked list member */ list_head_t e_list; } virtual_server_group_t; /* Virtual Server definition */ typedef struct _virtual_server { const char *vsgname; virtual_server_group_t *vsg; sockaddr_t addr; uint32_t vfwmark; real_server_t *s_svr; bool s_svr_duplicates_rs; uint16_t af; uint16_t service_type; bool ha_suspend; int ha_suspend_addr_count; char sched[IP_VS_SCHEDNAME_MAXLEN]; uint32_t flags; uint32_t persistence_timeout; char pe_name[IP_VS_PENAME_MAXLEN + 1]; unsigned forwarding_method; #ifdef _HAVE_IPVS_TUN_TYPE_ int tun_type; /* tunnel type */ unsigned tun_port; /* tunnel port for gue tunnels */ #ifdef _HAVE_IPVS_TUN_CSUM_ int tun_flags; /* tunnel checksum type for gue/gre tunnels */ #endif #endif #ifdef _WITH_SNMP_CHECKER_ const char *snmp_name; #endif uint32_t persistence_granularity; const char *virtualhost; /* Default virtualhost for HTTP and SSL healthcheckers if not set on real servers */ int weight; list_head_t rs; /* real_server_t */ int alive; bool alpha; /* Set if alpha mode is default. */ bool omega; /* Omega mode enabled. */ bool inhibit; /* Set weight to 0 instead of removing * the service from IPVS topology. */ unsigned int connection_to; /* connection time-out */ unsigned long delay_loop; /* Interval between running checker */ unsigned long warmup; /* max random timeout to start checker */ unsigned retry; /* number of retries before failing */ unsigned long delay_before_retry; /* interval between retries */ notify_script_t *notify_quorum_up; /* A hook to call when the VS gains quorum. */ notify_script_t *notify_quorum_down; /* A hook to call when the VS loses quorum. */ unsigned quorum; /* Minimum live RSs to consider VS up. */ unsigned hysteresis; /* up/down events "lag" WRT quorum. */ int smtp_alert; /* Send email on status change */ bool quorum_state_up; /* Reflects result of the last transition done. */ bool reloaded; /* quorum_state was copied from old config while reloading */ #if defined _WITH_SNMP_CHECKER_ /* Statistics */ struct timespec vs_stats_last_updated; struct timespec rs_stats_last_updated; unsigned rs_cnt; /* Number of real_server in list */ unsigned num_dests; /* Only needed if using old socket interface */ #ifndef _WITH_LVS_64BIT_STATS_ struct ip_vs_stats_user stats; #else struct ip_vs_stats64 stats; #endif #endif /* Linked list member */ list_head_t e_list; } virtual_server_t; /* Configuration data root */ typedef struct _check_data { bool ssl_required; ssl_data_t *ssl; list_head_t vs_group; /* virtual_server_group_t */ list_head_t vs; /* virtual_server_t */ list_head_t track_files; /* tracked_file_t */ #ifdef _WITH_BFD_ list_head_t track_bfds; /* checker_tracked_bfd_t */ #endif unsigned num_checker_fd_required; unsigned num_smtp_alert; } check_data_t; /* macro utility */ #define ISALIVE(S) ((S)->alive) #define SET_ALIVE(S) ((S)->alive = true) #define UNSET_ALIVE(S) ((S)->alive = false) #define FMT_RS(R, V) (format_rs(R, V)) #define FMT_VS(V) (format_vs((V))) #ifndef IP_VS_SVC_F_SCHED_MH_PORT #define IP_VS_SVC_F_SCHED_MH_PORT IP_VS_SVC_F_SCHED_SH_PORT #endif #ifndef IP_VS_SVC_F_SCHED_MH_FALLBACK #define IP_VS_SVC_F_SCHED_MH_FALLBACK IP_VS_SVC_F_SCHED_SH_FALLBACK #endif static inline int real_weight(int64_t effective_weight) { if (effective_weight < 0) return 0; if (effective_weight > IPVS_WEIGHT_LIMIT) return IPVS_WEIGHT_LIMIT; return effective_weight; } #ifdef _WITH_NFTABLES_ /* We need to ensure that the file/function/line logged relates to where * protocol_to_index() is called from. */ #define protocol_to_index(proto) protocol_to_index_flf(proto, __func__, __LINE__, __FILE__) static inline proto_index_t protocol_to_index_flf(int proto, const char *func, int line, const char *file) { if (proto == IPPROTO_TCP) return TCP_INDEX; if (proto == IPPROTO_UDP) return UDP_INDEX; if (proto == IPPROTO_SCTP) return SCTP_INDEX; log_message(LOG_INFO, "Unknown protocol %d in protocol_to_index() called from %s at %s:%d", proto, func, file, line); return UDP_INDEX; } #endif /* Global vars exported */ extern check_data_t *check_data; extern check_data_t *old_check_data; /* prototypes */ extern ssl_data_t *alloc_ssl(void) __attribute((malloc)); extern void free_ssl(void); extern virtual_server_group_t *alloc_vsg(const char *); extern void free_vsg(virtual_server_group_t *); extern void alloc_vsg_entry(const vector_t *); extern real_server_t *alloc_rs(const char *, const char *); extern void free_rs(real_server_t *); extern virtual_server_t *alloc_vs(const char *, const char *); extern void free_vs(virtual_server_t *); extern void dump_tracking_rs(FILE *, const void *); extern void alloc_ssvr(const char *, const char *); #ifdef _WITH_BFD_ extern void free_checker_bfd(checker_tracked_bfd_t *); #endif extern check_data_t *alloc_check_data(void); extern void free_check_data(check_data_t **); extern void dump_data_check(FILE *); extern const char *format_vs (const virtual_server_t *); extern const char *format_vsge (const virtual_server_group_entry_t *); extern const char *format_rs(const real_server_t *, const virtual_server_t *); extern bool validate_check_config(void); #endif keepalived-2.3.3/keepalived/include/check_ssl.h0000664000175000017500000000306513671420376015173 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_http.c include file. * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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, */ #ifndef _CHECK_SSL_H #define _CHECK_SSL_H /* system includes */ #include /* local includes */ #include "check_data.h" #include "scheduler.h" /* Prototypes */ extern void install_ssl_check_keyword(void); extern bool init_ssl_ctx(void); extern void clear_ssl(ssl_data_t *); extern int ssl_connect(thread_ref_t, int); extern int ssl_printerr(int); extern bool ssl_send_request(SSL *, const char *, int); extern void ssl_read_thread(thread_ref_t); #ifdef THREAD_DUMP extern void register_check_ssl_addresses(void); #endif #endif keepalived-2.3.3/keepalived/trackers/0000775000175000017500000000000014772274312013333 5keepalived-2.3.3/keepalived/trackers/Makefile.am0000664000175000017500000000105713666745231015316 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2020 Alexandre Cassen, AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib AM_CPPFLAGS += $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libtracker.a libtracker_a_SOURCES = \ track_file.c libtracker_a_LIBADD = EXTRA_libtracker_a_SOURCES = MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ keepalived-2.3.3/keepalived/trackers/Makefile.in0000664000175000017500000004271714772274255015341 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2020 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/trackers ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libtracker_a_AR = $(AR) $(ARFLAGS) libtracker_a_DEPENDENCIES = am_libtracker_a_OBJECTS = track_file.$(OBJEXT) libtracker_a_OBJECTS = $(am_libtracker_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/lib depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/track_file.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libtracker_a_SOURCES) $(EXTRA_libtracker_a_SOURCES) DIST_SOURCES = $(libtracker_a_SOURCES) $(EXTRA_libtracker_a_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib \ $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libtracker.a libtracker_a_SOURCES = \ track_file.c libtracker_a_LIBADD = EXTRA_libtracker_a_SOURCES = all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/trackers/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/trackers/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libtracker.a: $(libtracker_a_OBJECTS) $(libtracker_a_DEPENDENCIES) $(EXTRA_libtracker_a_DEPENDENCIES) $(AM_V_at)-rm -f libtracker.a $(AM_V_AR)$(libtracker_a_AR) libtracker.a $(libtracker_a_OBJECTS) $(libtracker_a_LIBADD) $(AM_V_at)$(RANLIB) libtracker.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/track_file.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/track_file.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/track_file.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-generic clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/trackers/track_file.c0000664000175000017500000007005314771257402015527 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Track file framework. * * Author: Alexandre Cassen, * * 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-2020 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "track_file.h" #include "tracker.h" #include "list_head.h" #include "vector.h" #include "parser.h" #include "bitops.h" #include "main.h" /* For reload */ #include "utils.h" /* For debug */ #include "logger.h" #ifdef _WITH_LVS_ #include "ipwrapper.h" #include "check_data.h" #endif #ifdef _WITH_VRRP_ #include "vrrp_scheduler.h" /* Possibly remove */ #include "vrrp_data.h" #endif /* The following definitions come from the statfs(2) man page. Some of them are defined in , others aren't. */ #ifndef BPF_FS_MAGIC #define BPF_FS_MAGIC 0xcafe4a11 #endif #ifndef CGROUP_SUPER_MAGIC #define CGROUP_SUPER_MAGIC 0x27e0eb #endif #ifndef CGROUP2_SUPER_MAGIC #define CGROUP2_SUPER_MAGIC 0x63677270 #endif #ifndef DEBUGFS_MAGIC #define DEBUGFS_MAGIC 0x64626720 #endif #ifndef DEVPTS_SUPER_MAGIC #define DEVPTS_SUPER_MAGIC 0x1cd1 #endif #ifndef EFIVARFS_MAGIC #define EFIVARFS_MAGIC 0xde5e81e4 #endif #ifndef FUSE_SUPER_MAGIC #define FUSE_SUPER_MAGIC 0x65735546 #endif #ifndef MQUEUE_MAGIC #define MQUEUE_MAGIC 0x19800202 #endif #ifndef NFS_SUPER_MAGIC #define NFS_SUPER_MAGIC 0x6969 #endif #ifndef PIPEFS_MAGIC #define PIPEFS_MAGIC 0x50495045 #endif #ifndef PROC_SUPER_MAGIC #define PROC_SUPER_MAGIC 0x9fa0 #endif #ifndef ROMFS_MAGIC #define ROMFS_MAGIC 0x7275 #endif #ifndef SELINUX_MAGIC #define SELINUX_MAGIC 0xf97cff8c #endif #ifndef SMB_SUPER_MAGIC #define SMB_SUPER_MAGIC 0x517b #endif #ifndef SMB2_MAGIC_NUMBER #define SMB2_MAGIC_NUMBER 0xfe534d42 #endif #ifndef SOCKFS_MAGIC #define SOCKFS_MAGIC 0x534f434b #endif #ifndef SYSFS_MAGIC #define SYSFS_MAGIC 0x62656572 #endif #ifndef SYSV2_SUPER_MAGIC #define SYSV2_SUPER_MAGIC 0x012ff7b6 #endif #ifndef SYSV4_SUPER_MAGIC #define SYSV4_SUPER_MAGIC 0x012ff7b5 #endif #ifndef TRACEFS_MAGIC #define TRACEFS_MAGIC 0x74726163 #endif /* Used for initialising track files */ static enum { TRACK_FILE_NO_INIT, TRACK_FILE_INIT, TRACK_FILE_OVERWRITE, } track_file_init; static int track_file_init_value; static tracked_file_t *current_tf; static int inotify_fd = -1; static thread_ref_t inotify_thread; /* Track file dump */ static void dump_track_file_monitor(FILE *fp, const tracked_file_monitor_t *tfile) { conf_write(fp, " %s, weight %d%s", tfile->file->fname, tfile->weight, tfile->weight_reverse ? " reverse" : ""); } void dump_track_file_monitor_list(FILE *fp, const list_head_t *l) { tracked_file_monitor_t *tfile; list_for_each_entry(tfile, l, e_list) dump_track_file_monitor(fp, tfile); } /* Configuration processing */ void free_track_file_monitor(tracked_file_monitor_t *tfile) { list_del_init(&tfile->e_list); FREE(tfile); } void free_track_file_monitor_list(list_head_t *l) { tracked_file_monitor_t *tfile, *tfile_tmp; list_for_each_entry_safe(tfile, tfile_tmp, l, e_list) free_track_file_monitor(tfile); } tracked_file_t * __attribute__ ((pure)) find_tracked_file_by_name(const char *name, list_head_t *l) { tracked_file_t *file; list_for_each_entry(file, l, e_list) { if (!strcmp(file->fname, name)) return file; } return NULL; } // Some of the following code is VRRP specific, and so should be in vrrp_track_file.c void vrrp_alloc_track_file(const char *name, list_head_t *tracked_files, list_head_t *track_file, const vector_t *strvec) { tracked_file_t *vsf; tracked_file_monitor_t *tfile; const char *tracked = strvec_slot(strvec, 0); tracked_file_monitor_t *etfile; int weight; bool reverse; vsf = find_tracked_file_by_name(tracked, tracked_files); /* Ignoring if no file found */ if (!vsf) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) track file %s not found, ignoring..." , name, tracked); return; } /* Check this object isn't already tracking the file */ list_for_each_entry(etfile, track_file, e_list) { if (etfile->file == vsf) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) duplicate track_file %s - ignoring" , name, tracked); return; } } weight = vsf->weight; reverse = vsf->weight_reverse; if (vector_size(strvec) >= 2) { if (strcmp(strvec_slot(strvec, 1), "weight")) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track file option %s - ignoring" , name, strvec_slot(strvec, 1)); return; } if (vector_size(strvec) == 2) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight without value specified" " for track file %s - ignoring" , name, tracked); return; } if (!read_int_strvec(strvec, 2, &weight, -254, 254, true)) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) weight for track file %s must be in " "[-254..254] inclusive. Ignoring...", name, tracked); weight = vsf->weight; } if (vector_size(strvec) >= 4) { if (!strcmp(strvec_slot(strvec, 3), "reverse")) reverse = true; else if (!strcmp(strvec_slot(strvec, 3), "noreverse")) reverse = false; else { report_config_error(CONFIG_GENERAL_ERROR, "(%s) unknown track file %s weight option %s - ignoring", name, tracked, strvec_slot(strvec, 3)); return; } } } PMALLOC(tfile); INIT_LIST_HEAD(&tfile->e_list); tfile->file = vsf; tfile->weight = weight; tfile->weight_reverse = reverse; list_add_tail(&tfile->e_list, track_file); } /* Parsers for track_file */ static void track_file_handler(const vector_t *strvec) { if (!strvec) return; /* Allocate new file structure */ PMALLOC(current_tf); INIT_LIST_HEAD(¤t_tf->e_list); INIT_LIST_HEAD(¤t_tf->tracking_obj); current_tf->fname = STRDUP(strvec_slot(strvec, 1)); current_tf->weight = 1; track_file_init = TRACK_FILE_NO_INIT; } #ifdef _WITH_VRRP_ static void vrrp_track_file_handler(const vector_t *strvec) { if (!strvec) return; log_message(LOG_INFO, "\"vrrp_track_file\" is deprecated, please use \"track_file\""); track_file_handler(strvec); } #endif static void track_file_file_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "track file file name missing"); return; } if (current_tf->file_path) { report_config_error(CONFIG_GENERAL_ERROR, "File already set for track file %s - ignoring %s" , current_tf->fname, strvec_slot(strvec, 1)); return; } current_tf->file_path = set_value(strvec); } static void track_file_weight_handler(const vector_t *strvec) { int weight; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No weight specified for track file %s - ignoring" , current_tf->fname); return; } if (current_tf->weight != 1) { report_config_error(CONFIG_GENERAL_ERROR, "Weight already set for track file %s - ignoring %s" , current_tf->fname, strvec_slot(strvec, 1)); return; } if (!read_int_strvec(strvec, 1, &weight, -254, 254, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Weight (%s) for track_file %s must be between " "[-254..254] inclusive. Ignoring..." , strvec_slot(strvec, 1), current_tf->fname); weight = 1; } current_tf->weight = weight; if (vector_size(strvec) >= 3) { if (!strcmp(strvec_slot(strvec, 2), "reverse")) current_tf->weight_reverse = true; else report_config_error(CONFIG_GENERAL_ERROR, "track_file %s unknown weight option %s" , current_tf->fname, strvec_slot(strvec, 2)); } } static void track_file_init_handler(const vector_t *strvec) { unsigned i; const char *word; int value; track_file_init = TRACK_FILE_INIT; track_file_init_value = 0; for (i = 1; i < vector_size(strvec); i++) { word = strvec_slot(strvec, i); word += strspn(word, WHITE_SPACE); if (isdigit(word[0]) || word[0] == '-') { if (!read_int_strvec(strvec, i, &value, INT_MIN, INT_MAX, false)) { /* It is not a valid integer */ report_config_error(CONFIG_GENERAL_ERROR, "Track file %s init value %s is invalid" , current_tf->fname, word); value = 0; } else if (value < -254 || value > 254) { // This is not valid for checker process report_config_error(CONFIG_GENERAL_ERROR, "Track file %s init value %d is" " outside sensible range [%d, %d]" , current_tf->fname, value, -254, 254); } track_file_init_value = value; } else if (!strcmp(word, "overwrite")) track_file_init = TRACK_FILE_OVERWRITE; else report_config_error(CONFIG_GENERAL_ERROR, "Unknown track file init option %s", word); } } static void track_file_end_handler(void) { struct stat statb; struct statfs fs_buf; FILE *tf; int ret; tracked_file_t *track_file; char *realpath_buf; char *dir_end; char *resolved_path; track_file = current_tf; current_tf = NULL; if (!track_file->file_path) { report_config_error(CONFIG_GENERAL_ERROR, "No file set for track_file %s - ignoring" , track_file->fname); FREE_CONST(track_file->fname); FREE(track_file); return; } /* Check the penultimate field in path is a directory */ if (!(dir_end = strrchr(track_file->file_path, '/'))) { realpath_buf = MALLOC(2); strcpy(realpath_buf, "."); } else { realpath_buf = MALLOC(dir_end - track_file->file_path + 1); strncpy(realpath_buf, track_file->file_path, dir_end - track_file->file_path); realpath_buf[dir_end - track_file->file_path] = '\0'; } resolved_path = realpath(realpath_buf, NULL); FREE(realpath_buf); if (!resolved_path) { report_config_error(CONFIG_GENERAL_ERROR, "realpath() error %d (%m) for %s", errno, track_file->fname); FREE_CONST(track_file->fname); FREE(track_file); return; } /* Check filesystem type. We can check for some that we know won't work. * The list of filesystem types checked can be expanded as needed. */ ret = statfs(resolved_path, &fs_buf); free(resolved_path); if (ret) { if (errno == ENOTDIR) { report_config_error(CONFIG_GENERAL_ERROR, "track file directory for %s " "does not exist - removing", track_file->fname); FREE_CONST(track_file->fname); FREE(track_file); return; } log_message(LOG_INFO, "statfs(%s) returned errno %d (%m)", track_file->fname, errno); } else { /* This is a workaround to the problem of fs_buf.f_type being unsigned int (32 bits) * on most 32 bit platforms, but signed long (64 bits) on 64 bit systems. * Without this workaround, comparison of fs_buf.f_type against, for example, * BPF_FS_MAGIC causes a signed/unsigned warning on 32 bit systems. * This also applies with EFIVARS_MAGIC, SELINUX_MAGIC and SMB2_MAGIC_NUMBER, * i.e. when bit 31 (0x80000000) is set. * * See /usr/include/asm-generic/statfs.h for more details. */ long long f_type = fs_buf.f_type; if (fs_buf.f_flags & ST_RDONLY || f_type == BPF_FS_MAGIC || f_type == CGROUP_SUPER_MAGIC || f_type == CGROUP2_SUPER_MAGIC || f_type == DEBUGFS_MAGIC || f_type == DEVPTS_SUPER_MAGIC || f_type == EFIVARFS_MAGIC || f_type == FUSE_SUPER_MAGIC || f_type == MQUEUE_MAGIC || f_type == NFS_SUPER_MAGIC || f_type == PIPEFS_MAGIC || f_type == PROC_SUPER_MAGIC || f_type == ROMFS_MAGIC || f_type == SELINUX_MAGIC || f_type == SMB_SUPER_MAGIC || f_type == SMB2_MAGIC_NUMBER || f_type == SOCKFS_MAGIC || f_type == SYSFS_MAGIC || f_type == SYSV2_SUPER_MAGIC || f_type == SYSV4_SUPER_MAGIC || f_type == TRACEFS_MAGIC) { /* The specified file is on a type of filesystem that inotify cannot monitor, * or the filesystem is read-only. */ report_config_error(CONFIG_GENERAL_ERROR, "The filesystem of %s is read only or cannot be monitored - ignoring", track_file->file_path); FREE_CONST(track_file->fname); FREE(track_file); return; } } if (track_file_init != TRACK_FILE_NO_INIT) { ret = stat(track_file->file_path, &statb); if (!ret && track_file_init == TRACK_FILE_OVERWRITE) { if ((statb.st_mode & S_IFMT) != S_IFREG) { /* It is not a regular file */ report_config_error(CONFIG_GENERAL_ERROR, "Cannot initialise track file %s" " - it is not a regular file" , track_file->fname); FREE_CONST(track_file->fname); FREE(track_file); return; } } /* Don't overwrite a file on reload */ if (!reload && !__test_bit(CONFIG_TEST_BIT, &debug) && (ret || track_file_init == TRACK_FILE_OVERWRITE)) { // the file doesn't exist or we want to overwrite it /* Write the value to the file */ if ((tf = fopen_safe(track_file->file_path, "w"))) { fprintf(tf, "%d\n", track_file_init_value); fclose(tf); } else report_config_error(CONFIG_GENERAL_ERROR, "Unable to initialise track file %s" , track_file->fname); } } #ifdef _WITH_VRRP_ if (vrrp_data) list_add_tail(&track_file->e_list, &vrrp_data->vrrp_track_files); #endif #ifdef _WITH_LVS_ if (check_data) { #if defined _ONE_PROCESS_DEBUG_ && defined _WITH_VRRP_ /* If we want it for both VRRP and LVS we must duplicate the data */ if (vrrp_data) { tracked_file_t *dup_track_file; PMALLOC(dup_track_file); *dup_track_file = *track_file; track_file = dup_track_file; } #endif list_add_tail(&track_file->e_list, &check_data->track_files); } #endif } void add_track_file_keywords(bool active) { /* Track file declarations */ install_keyword_root("track_file", &track_file_handler, active, VPP ¤t_tf); install_keyword("file", &track_file_file_handler); install_keyword("weight", &track_file_weight_handler); install_keyword("init_file", &track_file_init_handler); install_level_end_handler(&track_file_end_handler); #ifdef _WITH_VRRP_ install_keyword_root("vrrp_track_file", &vrrp_track_file_handler, active, VPP ¤t_tf); /* Deprecated synonym - after v2.0.20 */ install_keyword("file", &track_file_file_handler); install_keyword("weight", &track_file_weight_handler); install_keyword("init_file", &track_file_init_handler); install_level_end_handler(&track_file_end_handler); #endif } void free_tracking_obj_list(list_head_t *l) { tracking_obj_t *top, *top_tmp; list_for_each_entry_safe(top, top_tmp, l, e_list) free_tracking_obj(top); } static void free_track_file(tracked_file_t *file) { list_del_init(&file->e_list); free_tracking_obj_list(&file->tracking_obj); FREE_CONST(file->fname); FREE_CONST(file->file_path); FREE(file); } void free_track_file_list(list_head_t *l) { tracked_file_t *file, *file_tmp; list_for_each_entry_safe(file, file_tmp, l, e_list) free_track_file(file); } void dump_tracking_obj_list(FILE *fp, const list_head_t *l, obj_dump_func_t dump) { tracking_obj_t *top; if (list_empty(l)) return; conf_write(fp, " Tracking instances :"); list_for_each_entry(top, l, e_list) { if (dump) (*dump) (fp, top); } } static void dump_track_file(FILE *fp, const tracked_file_t *file) { conf_write(fp, " Track file = %s", file->fname); conf_write(fp, " File = %s", file->file_path); conf_write(fp, " Status = %" PRIi64, file->last_status); conf_write(fp, " Weight = %d%s", file->weight, file->weight_reverse ? " reverse" : ""); dump_tracking_obj_list(fp, &file->tracking_obj, file->tracking_obj_dump); } void dump_track_file_list(FILE *fp, const list_head_t *l) { tracked_file_t *file; list_for_each_entry(file, l, e_list) dump_track_file(fp, file); } void add_obj_to_track_file(void *obj, tracked_file_monitor_t *tfl, const char *name, obj_dump_func_t dump) { tracked_file_t *file = tfl->file; tracking_obj_t *top; if (!file) return; if (!file->tracking_obj_dump) file->tracking_obj_dump = dump; /* Is this file already tracking the vrrp instance directly? * For this to be the case, the file was added directly on the vrrp instance, * and now we are adding it for a sync group. */ list_for_each_entry(top, &file->tracking_obj, e_list) { if (top->obj.obj == obj) { /* Update the weight appropriately. We will use the sync group's * weight unless the vrrp setting is unweighted. */ log_message(LOG_INFO, "(%s) track_file %s is configured on VRRP instance and sync group. Remove vrrp instance config" , name, file->fname); if (top->weight) { top->weight = tfl->weight; top->weight_multiplier = tfl->weight_reverse ? -1 : 1; } return; } } PMALLOC(top); INIT_LIST_HEAD(&top->e_list); top->obj.obj = obj; top->weight = tfl->weight; top->weight_multiplier = tfl->weight_reverse ? -1 : 1; list_add_tail(&top->e_list, &file->tracking_obj); } static void remove_track_file(tracked_file_t *file) { tracked_file_monitor_t *tft, *tft_tmp; list_head_t *track_file_list; tracking_obj_t *top; /* Search through the objects tracking this file */ list_for_each_entry(top, &file->tracking_obj, e_list) { #ifdef _WITH_VRRP_ if (vrrp_data) track_file_list = &top->obj.vrrp->track_file; else #endif #ifdef _WITH_LVS_ if (check_data) track_file_list = &top->obj.checker->rs->track_files; else #endif break; /* Search for the matching track file */ list_for_each_entry_safe(tft, tft_tmp, track_file_list, e_list) { if (tft->file == file) { free_track_file_monitor(tft); break; } } } free_track_file(file); } #ifdef _WITH_VRRP_ static void process_update_vrrp_track_file_status(const tracked_file_t *tfile, int new_status, const tracking_obj_t *top) { int previous_status; vrrp_t *vrrp = top->obj.vrrp; if (new_status < -254) new_status = -254; else if (new_status > 253) new_status = 253; previous_status = !top->weight ? (!!tfile->last_status == (top->weight_multiplier == 1) ? -254 : 0 ) : tfile->last_status * top->weight * top->weight_multiplier; #ifdef TMP_TRACK_FILE_DEBUG log_message(LOG_INFO, "top->weight %d, mult %d tfile->last_status %" PRIi64 ", previous_status %d new_status %d" , top->weight, top->weight_multiplier, tfile->last_status, previous_status, new_status); #endif if (previous_status < -254) previous_status = -254; else if (previous_status > 253) previous_status = 253; if (previous_status == new_status) return; if (new_status == -254) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s): tracked file %s now FAULT state" , vrrp->iname, tfile->fname); if (top->weight) vrrp->total_priority -= previous_status; down_instance(vrrp, VRRP_FAULT_FL_TRACKER); } else if (previous_status == -254) { if (top->weight) { vrrp->total_priority += new_status; vrrp->effective_priority = vrrp->total_priority >= VRRP_PRIO_OWNER ? VRRP_PRIO_OWNER - 1 : vrrp->total_priority < 1 ? 1 : vrrp->total_priority; } if (__test_bit(LOG_DETAIL_BIT, &debug)) { log_message(LOG_INFO, "(%s): tracked file %s leaving FAULT state" , vrrp->iname, tfile->fname); if (new_status) log_message(LOG_INFO, "(%s) Setting effective priority to %d" , vrrp->iname, vrrp->effective_priority); } try_up_instance(vrrp, false, VRRP_FAULT_FL_TRACKER); } else { vrrp->total_priority += new_status - previous_status; vrrp_set_effective_priority(vrrp); } } #endif #ifdef _WITH_LVS_ void process_update_checker_track_file_status(const tracked_file_t *tfile, int new_status, const tracking_obj_t *top) { int previous_status; int64_t previous_status64; checker_t *checker = top->obj.checker; previous_status64 = !top->weight ? (!tfile->last_status != (top->weight_multiplier == 1) ? IPVS_WEIGHT_FAULT : 0 ) : (int64_t)tfile->last_status * top->weight * top->weight_multiplier; previous_status = weight_range(previous_status64); if (previous_status == new_status) return; if (new_status == IPVS_WEIGHT_FAULT) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s): tracked file %s now FAULT state" , FMT_RS(checker->rs, checker->vs), tfile->fname); update_svr_checker_state(DOWN, checker); checker->rs->effective_weight -= checker->cur_weight; checker->cur_weight = 0; } else if (previous_status == IPVS_WEIGHT_FAULT) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s): tracked file %s leaving FAULT state" , FMT_RS(checker->rs, checker->vs), tfile->fname); checker->cur_weight = new_status; checker->rs->effective_weight += new_status; update_svr_checker_state(UP, checker); } else { #ifdef TMP_TRACK_FILE_DEBUG log_message(LOG_INFO, "Updated weight to %" PRIi64 " (weight %d, new_status %d previous_status %d)" , checker->rs->effective_weight + new_status - previous_status , real_weight(checker->rs->effective_weight), new_status, previous_status); #endif update_svr_wgt(checker->rs->effective_weight + new_status - previous_status, checker->vs, checker->rs, true); checker->cur_weight = new_status; } } #endif static void update_track_file_status(tracked_file_t *tfile, int64_t new_status) { tracking_obj_t *top; int status; if (new_status == tfile->last_status) return; /* Process the objects tracking the file */ list_for_each_entry(top, &tfile->tracking_obj, e_list) { /* If the tracking weight is 0, a non-zero value means * failure, a 0 status means success */ if (!top->weight) status = !new_status != (top->weight_multiplier == 1) ? INT_MIN : 0; else status = weight_range((int64_t)new_status * top->weight * top->weight_multiplier); #ifdef _WITH_VRRP_ if (vrrp_data) process_update_vrrp_track_file_status(tfile, status, top); #endif #ifdef _WITH_LVS_ if (check_data) process_update_checker_track_file_status(tfile, status, top); #endif } } static void process_track_file(tracked_file_t *tfile, bool init) { int64_t new_status = 0; char buf[128]; int fd; ssize_t len; if ((fd = open(tfile->file_path, O_RDONLY | O_NONBLOCK)) != -1) { len = read(fd, buf, sizeof(buf) - 1); close(fd); if (len > 0) { buf[len] = '\0'; /* If there is an error, we want to use 0, * so we don't really mind if there is an error */ errno = 0; #if LONG_MAX > INT32_MAX new_status = strtol(buf, NULL, 0); #else new_status = strtoll(buf, NULL, 0); #endif if (errno || new_status < (int64_t)INT32_MIN || new_status > (int64_t)INT32_MAX + 1) { log_message(LOG_INFO, "Invalid number %" PRId64 " read from %s - ignoring", new_status, tfile->file_path); return; } } } if (!init) update_track_file_status(tfile, new_status); #ifdef TMP_TRACK_FILE_DEBUG log_message(LOG_INFO, "Read %s: long val %ld, val %d, last status %" PRIi64 , tfile->file_path, new_status, (int)new_status, tfile->last_status); #endif tfile->last_status = new_status; } static void process_inotify(thread_ref_t thread) { char buf[sizeof(struct inotify_event) + NAME_MAX + 1] __attribute__((aligned(__alignof__(struct inotify_event)))); char *buf_ptr; ssize_t len; struct inotify_event* event; tracked_file_t *tfile; int fd = thread->u.f.fd; list_head_t *track_files = thread->arg; inotify_thread = thread_add_read(master, process_inotify, track_files, fd, TIMER_NEVER, 0); while (true) { if ((len = read(fd, buf, sizeof(buf))) < (ssize_t)sizeof(struct inotify_event)) { if (len == -1) { if (check_EAGAIN(errno)) return; if (check_EINTR(errno)) continue; log_message(LOG_INFO, "inotify read() returned error %d - %m", errno); return; } log_message(LOG_INFO, "inotify read() returned short length %zd", len); return; } /* Try and keep coverity happy. It thinks event->name is not null * terminated in the strcmp() below */ buf[sizeof(buf) - 1] = 0; /* The following line causes a strict-overflow=4 warning on gcc 5.4.0 */ for (buf_ptr = buf; buf_ptr < buf + len; buf_ptr += event->len + sizeof(struct inotify_event)) { event = PTR_CAST(struct inotify_event, buf_ptr); /* We are not interested in directories */ if (event->mask & IN_ISDIR) continue; if (!(event->mask & (IN_DELETE | IN_CLOSE_WRITE | IN_MOVE))) { log_message(LOG_INFO, "Unknown inotify event 0x%x", event->mask); continue; } list_for_each_entry(tfile, track_files, e_list) { /* Is this event for our file */ if (tfile->wd != event->wd || strcmp(tfile->file_part, event->name)) continue; if (event->mask & (IN_MOVED_FROM | IN_DELETE)) { /* The file has disappeared. Treat as though the value is 0 */ update_track_file_status(tfile, 0); tfile->last_status = 0; } else { /* event->mask & (IN_MOVED_TO | IN_CLOSE_WRITE) */ /* The file has been writted/moved in */ process_track_file(tfile, false); } } } } /* NOT REACHED */ } void init_track_files(list_head_t *track_files) { tracked_file_t *tfile, *tfile_tmp; char *resolved_path; char *dir_end = NULL; char *new_path; struct stat stat_buf; char *realpath_buf; bool file_exists; if (inotify_fd != -1) { /* This should not happen */ close(inotify_fd); inotify_fd = -1; } realpath_buf = MALLOC(PATH_MAX); list_for_each_entry_safe(tfile, tfile_tmp, track_files, e_list) { if (list_empty(&tfile->tracking_obj)) { /* Nothing is tracking this file, so forget it */ remove_track_file(tfile); continue; } file_exists = false; resolved_path = realpath(tfile->file_path, realpath_buf); if (resolved_path) { if (strcmp(tfile->file_path, resolved_path)) { FREE_CONST(tfile->file_path); tfile->file_path = STRDUP(resolved_path); } file_exists = true; } else if (errno == ENOENT) { /* Resolve the directory */ if (!(dir_end = strrchr(tfile->file_path, '/'))) resolved_path = realpath(".", realpath_buf); else { *dir_end = '\0'; resolved_path = realpath(tfile->file_path, realpath_buf); /* Check it is a directory */ if (resolved_path && (stat(resolved_path, &stat_buf) || !S_ISDIR(stat_buf.st_mode))) { resolved_path = NULL; } } if (!resolved_path) { report_config_error(CONFIG_GENERAL_ERROR, "Track file directory for %s " "does not exist - removing" , tfile->fname); remove_track_file(tfile); continue; } /* Make the file name with the resolved directory path */ if (strcmp(tfile->file_path, resolved_path)) { new_path = MALLOC(strlen(resolved_path) + strlen((!dir_end) ? tfile->file_path : dir_end + 1) + 2); strcpy(new_path, resolved_path); strcat(new_path, "/"); strcat(new_path, dir_end ? dir_end + 1 : tfile->file_path); FREE_CONST(tfile->file_path); tfile->file_path = new_path; } else if (dir_end) *dir_end = '/'; } else { report_config_error(CONFIG_GENERAL_ERROR, "track file %s is not accessible" " - ignoring", tfile->fname); remove_track_file(tfile); continue; } if (inotify_fd == -1) { inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); if (inotify_fd == -1) { log_message(LOG_INFO, "Unable to monitor track files"); break; } } tfile->file_part = strrchr(tfile->file_path, '/') + 1; new_path = STRNDUP(tfile->file_path, tfile->file_part - tfile->file_path); tfile->wd = inotify_add_watch(inotify_fd, new_path, IN_CLOSE_WRITE | IN_DELETE | IN_MOVE); FREE(new_path); /* If the file exists, read it now */ if (file_exists) process_track_file(tfile, true); } FREE(realpath_buf); if (inotify_fd != -1) inotify_thread = thread_add_read(master, process_inotify, track_files, inotify_fd, TIMER_NEVER, 0); } void stop_track_files(void) { if (inotify_thread) { thread_cancel(inotify_thread); inotify_thread = NULL; } if (inotify_fd != -1) { close(inotify_fd); inotify_fd = -1; } } #ifdef THREAD_DUMP void register_track_file_inotify_addresses(void) { register_thread_address("process_inotify", process_inotify); } #endif keepalived-2.3.3/keepalived/bfd/0000775000175000017500000000000014772274312012250 5keepalived-2.3.3/keepalived/bfd/bfd_data.c0000664000175000017500000002621514714144150014056 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Dynamic data structure definition * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #include "config.h" #include "bfd.h" #include "global_data.h" #include "bfd_data.h" #include "logger.h" #include "parser.h" #include "memory.h" #include "utils.h" #include "main.h" #include "assert_debug.h" /* Global vars */ bfd_data_t *bfd_data; bfd_data_t *old_bfd_data; char *bfd_buffer; /* * bfd_t functions */ /* Initialize bfd_t */ bfd_t * alloc_bfd(const char *name) { bfd_t *bfd; assert(name); if (strlen(name) >= sizeof(bfd->iname)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " name too long (maximum length is %zu" " characters) - ignoring", name, sizeof(bfd->iname) - 1); return NULL; } if (find_bfd_by_name(name)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " already configured - ignoring", name); return NULL; } PMALLOC(bfd); INIT_LIST_HEAD(&bfd->e_list); strcpy(bfd->iname, name); /* Set defaults */ bfd->local_min_rx_intv = BFD_MINRX_DEFAULT * TIMER_HZ / 1000; bfd->local_min_tx_intv = BFD_MINTX_DEFAULT * TIMER_HZ / 1000; bfd->local_idle_tx_intv = BFD_IDLETX_DEFAULT * TIMER_HZ / 1000; bfd->local_detect_mult = BFD_MULTIPLIER_DEFAULT; bfd->ttl = 0; bfd->max_hops = 0; /* Initialize internal variables */ bfd->fd_out = -1; bfd->thread_open_fd_out = NULL; bfd->thread_out = NULL; bfd->thread_exp = NULL; bfd->thread_rst = NULL; bfd->sands_out = TIMER_NEVER; bfd->sands_exp = TIMER_NEVER; bfd->sands_rst = TIMER_NEVER; return bfd; } void free_bfd(bfd_t *bfd) { list_del_init(&bfd->e_list); FREE(bfd); } static void free_bfd_list(list_head_t *l) { bfd_t *bfd, *bfd_tmp; list_for_each_entry_safe(bfd, bfd_tmp, l, e_list) free_bfd(bfd); } static void conf_write_sands(FILE *fp, const char *text, unsigned long sands) { char time_str[26]; time_t secs; if (sands == TIMER_NEVER) { conf_write(fp, " %s = [disabled]", text); return; } secs = sands / TIMER_HZ; if (!ctime_r(&secs, time_str)) strcpy(time_str, "invalid time "); conf_write(fp, " %s = %" PRI_time_t ".%6.6lu (%.19s.%6.6lu)", text, secs, sands % TIMER_HZ, time_str, sands % TIMER_HZ); } /* Dump BFD instance configuration parameters */ static void dump_bfd(FILE *fp, const bfd_t *bfd) { char time_str[26]; conf_write(fp, " BFD Instance = %s", bfd->iname); conf_write(fp, " Neighbor IP = %s", inet_sockaddrtos(&bfd->nbr_addr)); if (bfd->src_addr.ss_family != AF_UNSPEC) conf_write(fp, " Source IP = %s", inet_sockaddrtos(&bfd->src_addr)); conf_write(fp, " Required min RX interval = %u us", bfd->local_min_rx_intv); conf_write(fp, " Desired min TX interval = %u us", bfd->local_min_tx_intv); conf_write(fp, " Desired idle TX interval = %u us", bfd->local_idle_tx_intv); conf_write(fp, " Detection multiplier = %d", bfd->local_detect_mult); conf_write(fp, " %s = %d", bfd->nbr_addr.ss_family == AF_INET ? "TTL" : "hoplimit", bfd->ttl); conf_write(fp, " max_hops = %d", bfd->max_hops); conf_write(fp, " passive = %s", bfd->passive ? "true" : "false"); #ifdef _WITH_VRRP_ conf_write(fp, " send event to VRRP process = %s", bfd->vrrp ? "Yes" : "No"); #endif #ifdef _WITH_LVS_ conf_write(fp, " send event to checker process = %s", bfd->checker ? "Yes" : "No"); #endif /* If this is not at startup time, write some state variables */ if (fp) { conf_write(fp, " fd_out %d", bfd->fd_out); conf_write(fp, " thread_open_fd_out 0x%p", bfd->thread_open_fd_out); conf_write(fp, " thread_out 0x%p", bfd->thread_out); conf_write_sands(fp, "sands_out", bfd->sands_out); conf_write(fp, " thread_exp 0x%p", bfd->thread_exp); conf_write_sands(fp, "sands_exp", bfd->sands_exp); conf_write(fp, " thread_rst 0x%p", bfd->thread_rst); conf_write_sands(fp, "sands_rst", bfd->sands_rst); conf_write(fp, " send error = %s", bfd->send_error ? "true" : "false"); conf_write(fp, " local state = %s", BFD_STATE_STR(bfd->local_state)); conf_write(fp, " remote state = %s", BFD_STATE_STR(bfd->remote_state)); conf_write(fp, " local discriminator = 0x%x", bfd->local_discr); conf_write(fp, " remote discriminator = 0x%x", bfd->remote_discr); conf_write(fp, " local diag = %s", BFD_DIAG_STR(bfd->local_diag)); conf_write(fp, " remote diag = %s", BFD_DIAG_STR(bfd->remote_diag)); conf_write(fp, " remote min tx intv = %u us", bfd->remote_min_tx_intv); conf_write(fp, " remote min rx intv = %u us", bfd->remote_min_rx_intv); conf_write(fp, " local demand = %u", bfd->local_demand); conf_write(fp, " remote demand = %u", bfd->remote_demand); conf_write(fp, " remote detect multiplier = %u", bfd->remote_detect_mult); conf_write(fp, " %spoll, %sfinal", bfd->poll ? "" : "!", bfd->final ? "" : "!"); conf_write(fp, " local tx intv = %u us", bfd->local_tx_intv); conf_write(fp, " remote tx intv = %u us", bfd->remote_tx_intv); conf_write(fp, " local detection time = %" PRIu64 " us", bfd->local_detect_time); conf_write(fp, " remote detection time = %" PRIu64 " us", bfd->remote_detect_time); if (bfd->last_seen.tv_sec == 0) conf_write(fp, " last_seen = [never]"); else { ctime_r(&bfd->last_seen.tv_sec, time_str); conf_write(fp, " last seen = %" PRI_tv_sec ".%6.6" PRI_tv_usec " (%.24s.%6.6" PRI_tv_usec ")", bfd->last_seen.tv_sec, bfd->last_seen.tv_usec, time_str, bfd->last_seen.tv_usec); } } } static void dump_bfd_list(FILE *fp, const list_head_t *l) { bfd_t *bfd; list_for_each_entry(bfd, l, e_list) dump_bfd(fp, bfd); } /* * Looks up bfd instance by name */ static bfd_t * __attribute__ ((pure)) find_bfd_by_name2(const char *name, const bfd_data_t *data) { bfd_t *bfd; assert(name); assert(data); list_for_each_entry(bfd, &data->bfd, e_list) { if (!strcmp(name, bfd->iname)) return bfd; } return NULL; } bfd_t * __attribute__ ((pure)) find_bfd_by_name(const char *name) { return find_bfd_by_name2(name, bfd_data); } /* compares old and new timers, returns 0 if they are the same */ static int bfd_cmp_timers(bfd_t * old_bfd, bfd_t * bfd) { return (old_bfd->local_min_rx_intv != bfd->local_min_rx_intv || old_bfd->local_min_tx_intv != bfd->local_min_tx_intv); } /* * bfd_data_t functions */ bfd_data_t * alloc_bfd_data(void) { bfd_data_t *data; PMALLOC(data); INIT_LIST_HEAD(&data->bfd); /* Initialize internal variables */ data->thread_in = NULL; data->fd_in = -1; data->multihop_fd_in = -1; return data; } void free_bfd_data(bfd_data_t **datap) { bfd_data_t *data = *datap; assert(data); free_bfd_list(&data->bfd); FREE(data); *datap = NULL; } void dump_bfd_data(FILE *fp, const bfd_data_t *data) { assert(data); dump_global_data(fp, global_data); if (fp) { conf_write(fp, "------< BFD Data >------"); if (data->fd_in != -1) conf_write(fp, " fd_in = %d", data->fd_in); if (data->multihop_fd_in != -1) conf_write(fp, " multihop fd_in = %d", data->multihop_fd_in); conf_write(fp, " thread_in = 0x%p", data->thread_in); } if (!list_empty(&data->bfd)) { conf_write(fp, "------< BFD Topology >------"); dump_bfd_list(fp, &data->bfd); } } #ifndef _ONE_PROCESS_DEBUG_ void dump_bfd_data_global(FILE *fp) { dump_bfd_data(fp, bfd_data); } #endif void bfd_print_data(void) { FILE *fp; fp = open_dump_file("_bfd"); if (!fp) return; dump_bfd_data(fp, bfd_data); fclose(fp); } void bfd_complete_init(void) { bfd_t *bfd, *bfd_old; assert(bfd_data); /* Build configuration */ list_for_each_entry(bfd, &bfd_data->bfd, e_list) { /* If there was an old instance with the same name copy its state and thread sands during reload */ if (reload && (bfd_old = find_bfd_by_name2(bfd->iname, old_bfd_data))) { bfd_copy_state(bfd, bfd_old, true); bfd_copy_sands(bfd, bfd_old); if (bfd_cmp_timers(bfd_old, bfd)) bfd_set_poll(bfd); } else bfd_init_state(bfd); } /* Copy old input fd on reload */ if (reload) { bfd_data->fd_in = old_bfd_data->fd_in; bfd_data->multihop_fd_in = old_bfd_data->multihop_fd_in; } } /* * bfd_buffer functions */ void alloc_bfd_buffer(void) { if (!bfd_buffer) bfd_buffer = (char *)MALLOC(BFD_BUFFER_SIZE); } void free_bfd_buffer(void) { FREE(bfd_buffer); bfd_buffer = NULL; } /* * Lookup functions */ /* Looks up bfd instance by neighbor address and port, and optional local address. * If local address is not set, then it is a configuration time check and * the bfd instance is configured without a local address. */ bfd_t * __attribute__ ((pure)) find_bfd_by_addr(const sockaddr_t *nbr_addr, const sockaddr_t *local_addr, bool multihop) { bfd_t *bfd; assert(nbr_addr); assert(local_addr); assert(bfd_data); list_for_each_entry(bfd, &bfd_data->bfd, e_list) { if (&bfd->nbr_addr == nbr_addr) continue; if (inet_sockaddrcmp(&bfd->nbr_addr, nbr_addr)) continue; if (multihop != bfd->multihop) continue; if (!bfd->src_addr.ss_family) return bfd; if (!local_addr->ss_family) { /* A new bfd instance without an address is being configured, * but we already have the neighbor address configured. */ return bfd; } if (!inet_sockaddrcmp(&bfd->src_addr, local_addr)) return bfd; } return NULL; } /* Looks up bfd instance by local discriminator */ bfd_t * __attribute__ ((pure)) find_bfd_by_discr(const uint32_t discr) { bfd_t *bfd; list_for_each_entry(bfd, &bfd_data->bfd, e_list) { if (bfd->local_discr == discr) return bfd; } return NULL; } /* * Utility functions */ /* Check if uint64_t is large enough to hold uint32_t * int */ #if INT_MAX <= INT32_MAX #define CALC_TYPE uint64_t #else #define CALC_TYPE double #endif /* Generates a random number in the specified interval */ uint32_t rand_intv(uint32_t min, uint32_t max) { /* coverity[dont_call] */ return (uint32_t)((((CALC_TYPE)max - min + 1) * random()) / (RAND_MAX + 1U)) + min; } #undef CALC_TYPE /* Returns random disciminator number */ uint32_t bfd_get_random_discr(bfd_data_t *data) { bfd_t *bfd; uint32_t discr; assert(data); do { /* rand_intv(1, UINT32_MAX) only returns even numbers, since * RAND_MAX == INT32_MAX == UINT32_MAX / 2 */ discr = (rand_intv(1, UINT32_MAX) & ~1) | (time_now.tv_sec & 1); /* Check for collisions */ list_for_each_entry(bfd, &data->bfd, e_list) { if (bfd->local_discr == discr) { discr = 0; break; } } } while (!discr); return discr; } keepalived-2.3.3/keepalived/bfd/bfd_event.c0000664000175000017500000000443613732154322014270 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: BFD event handling * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #include "config.h" #include #include "bfd.h" #include "bfd_event.h" #include "bfd_daemon.h" #include "logger.h" #include "main.h" #include "memory.h" #include "bitops.h" #include "utils.h" #include "global_data.h" #include "assert_debug.h" void bfd_event_send(bfd_t *bfd) { bfd_event_t evt; int ret; #ifdef _WITH_VRRP_ bool vrrp_running = running_vrrp(); #endif #ifdef _WITH_LVS_ bool checker_running = running_checker(); #endif assert(bfd); /* If there is no VRRP process running, don't write to the pipe */ if (true #ifdef _WITH_VRRP_ && !vrrp_running #endif #ifdef _WITH_LVS_ && !checker_running #endif ) return; memset(&evt, 0, sizeof evt); strcpy(evt.iname, bfd->iname); evt.state = bfd->local_state == BFD_STATE_UP ? BFD_STATE_UP : BFD_STATE_DOWN; evt.sent_time = timer_now(); #ifdef _WITH_VRRP_ if (vrrp_running && bfd->vrrp) { ret = write(bfd_vrrp_event_pipe[1], &evt, sizeof evt); if (ret == -1 && __test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "(%s) vrrp pipe write() error %m", bfd->iname); } #endif #ifdef _WITH_LVS_ if (checker_running && bfd->checker) { ret = write(bfd_checker_event_pipe[1], &evt, sizeof evt); if (ret == -1 && __test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "(%s) checker pipe write() error %m", bfd->iname); } #endif } keepalived-2.3.3/keepalived/bfd/Makefile.am0000664000175000017500000000113413666745231014227 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2017-2017 Alexandre Cassen, AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib AM_CPPFLAGS += $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libbfd.a libbfd_a_SOURCES = \ bfd.c bfd_data.c bfd_parser.c bfd_daemon.c bfd_scheduler.c \ bfd_event.c EXTRA_libbfd_a_SOURCES = libbfd_a_LIBADD = MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ keepalived-2.3.3/keepalived/bfd/bfd_daemon.c0000664000175000017500000003131614706175715014422 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: BFD child process handling * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include #include #ifdef _WITH_PROFILING_ #include #endif #include "bfd.h" #include "bfd_daemon.h" #include "bfd_data.h" #include "bfd_parser.h" #include "bfd_scheduler.h" #include "bfd_event.h" #include "pidfile.h" #include "logger.h" #include "signals.h" #include "main.h" #include "parser.h" #include "time.h" #include "global_data.h" #include "bitops.h" #include "utils.h" #include "scheduler.h" #include "process.h" #include "utils.h" #ifdef _WITH_TRACK_PROCESS_ #include "track_process.h" #endif #ifdef _USE_SYSTEMD_NOTIFY_ #include "systemd.h" #endif #ifndef _ONE_PROCESS_DEBUG_ #include "config_notify.h" #endif /* Global variables */ int bfd_vrrp_event_pipe[2] = { -1, -1}; int bfd_checker_event_pipe[2] = { -1, -1}; /* Local variables */ static const char *bfd_syslog_ident; #ifndef _ONE_PROCESS_DEBUG_ static void reload_bfd_thread(thread_ref_t); static timeval_t bfd_start_time; static unsigned bfd_next_restart_delay; #endif /* Daemon stop sequence */ static void stop_bfd(int status) { if (__test_bit(CONFIG_TEST_BIT, &debug)) return; /* Stop daemon */ pidfile_rm(&bfd_pidfile); /* Clean data */ free_global_data(&global_data); bfd_dispatcher_release(bfd_data); free_bfd_data(&bfd_data); free_bfd_buffer(); thread_destroy_master(master); free_parent_mallocs_exit(); /* * Reached when terminate signal catched. * finally return to parent process. */ log_stopping(); #ifdef ENABLE_LOG_TO_FILE if (log_file_name) close_log_file(); #endif closelog(); #ifndef _MEM_CHECK_LOG_ FREE_CONST_PTR(bfd_syslog_ident); #else if (bfd_syslog_ident) free(no_const_char_p(bfd_syslog_ident)); /* malloc'd by make_syslog_ident() */ #endif close_std_fd(); exit(status); } /* Daemon init sequence */ bool open_bfd_pipes(void) { #ifdef _WITH_VRRP_ /* Open BFD VRRP control pipe */ if (open_pipe(bfd_vrrp_event_pipe) == -1) { log_message(LOG_ERR, "Unable to create BFD vrrp event pipe: %m"); return false; } #endif #ifdef _WITH_LVS_ /* Open BFD checker control pipe */ if (open_pipe(bfd_checker_event_pipe) == -1) { log_message(LOG_ERR, "Unable to create BFD checker event pipe: %m"); return false; } #endif return true; } /* Daemon init sequence */ static void start_bfd(__attribute__((unused)) data_t *prev_global_data) { srandom(time(NULL)); if (reload) global_data = alloc_global_data(); if (!(bfd_data = alloc_bfd_data())) { stop_bfd(KEEPALIVED_EXIT_FATAL); return; } alloc_bfd_buffer(); init_data(conf_file, bfd_init_keywords, false); if (reload) init_global_data(global_data, prev_global_data, true); /* Update process name if necessary */ if ((!prev_global_data && // startup global_data->bfd_process_name) || (prev_global_data && // reload (!global_data->bfd_process_name != !prev_global_data->bfd_process_name || (global_data->bfd_process_name && strcmp(global_data->bfd_process_name, prev_global_data->bfd_process_name))))) set_process_name(global_data->bfd_process_name); /* If we are just testing the configuration, then we terminate now */ if (__test_bit(CONFIG_TEST_BIT, &debug)) return; bfd_complete_init(); #ifndef _ONE_PROCESS_DEBUG_ if (global_data->reload_check_config && get_config_status() != CONFIG_OK) { stop_bfd(KEEPALIVED_EXIT_CONFIG); return; } /* Notify parent config has been read if appropriate */ if (!__test_bit(CONFIG_TEST_BIT, &debug)) notify_config_read(); #endif if (__test_bit(DUMP_CONF_BIT, &debug)) dump_bfd_data(NULL, bfd_data); thread_add_event(master, bfd_dispatcher_init, bfd_data, 0); /* Set the process priority and non swappable if configured */ // TODO - measure max stack usage set_process_priorities( global_data->bfd_realtime_priority, global_data->max_auto_priority, global_data->min_auto_priority_delay, global_data->bfd_rlimit_rt, global_data->bfd_process_priority, global_data->bfd_no_swap ? 4096 : 0); /* Set the process cpu affinity if configured */ set_process_cpu_affinity(&global_data->bfd_cpu_mask, "bfd"); } void bfd_validate_config(void) { start_bfd(NULL); } #ifndef _ONE_PROCESS_DEBUG_ static void print_bfd_thread(__attribute__((unused)) thread_ref_t thread) { bfd_print_data(); } /* Reload handler */ static void sigreload_bfd(__attribute__ ((unused)) void *v, __attribute__ ((unused)) int sig) { thread_add_event(master, reload_bfd_thread, NULL, 0); } static void sigdump_bfd(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { log_message(LOG_INFO, "Printing BFD data for process(%d) on signal", getpid()); thread_add_event(master, print_bfd_thread, NULL, 0); } /* Terminate handler */ static void sigend_bfd(__attribute__ ((unused)) void *v, __attribute__ ((unused)) int sig) { if (master) thread_add_terminate_event(master); } /* BFD Child signal handling */ static void bfd_signal_init(void) { signal_set(SIGHUP, sigreload_bfd, NULL); if (ignore_sigint) signal_ignore(SIGINT); else signal_set(SIGINT, sigend_bfd, NULL); signal_set(SIGTERM, sigend_bfd, NULL); signal_set(SIGUSR1, sigdump_bfd, NULL); #ifdef THREAD_DUMP signal_set(SIGTDUMP, thread_dump_signal, NULL); #endif signal_ignore(SIGPIPE); } /* Reload thread */ static void reload_bfd_thread(__attribute__((unused)) thread_ref_t thread) { timeval_t timer; timer = timer_now(); log_message(LOG_INFO, "Reloading"); /* Use standard scheduling while reloading */ reset_process_priorities(); #ifndef _ONE_PROCESS_DEBUG_ save_config(false, "bfd", dump_bfd_data_global); #endif /* set the reloading flag */ SET_RELOAD; /* Destroy master thread */ bfd_dispatcher_release(bfd_data); thread_cleanup_master(master, true); thread_add_base_threads(master, false); old_bfd_data = bfd_data; bfd_data = NULL; old_global_data = global_data; global_data = NULL; reinitialise_global_vars(); /* Reload the conf */ signal_set(SIGCHLD, thread_child_handler, master); start_bfd(old_global_data); free_bfd_data(&old_bfd_data); free_global_data(&old_global_data); #ifndef _ONE_PROCESS_DEBUG_ save_config(true, "bfd", dump_bfd_data_global); #endif UNSET_RELOAD; set_time_now(); log_message(LOG_INFO, "Reload finished in %lu usec", -timer_long(timer_sub_now(timer))); /* Post initializations */ #ifdef _MEM_CHECK_ log_message(LOG_INFO, "Configuration is using : %zu Bytes", get_keepalived_cur_mem_allocated()); #endif } /* This function runs in the parent process. */ static void delayed_restart_bfd_child_thread(__attribute__((unused)) thread_ref_t thread) { start_bfd_child(); } /* BFD Child respawning thread. This function runs in the parent process. */ static void bfd_respawn_thread(thread_ref_t thread) { unsigned restart_delay; int ret; /* We catch a SIGCHLD, handle it */ bfd_child = 0; if ((ret = report_child_status(thread->u.c.status, thread->u.c.pid, NULL))) thread_add_parent_terminate_event(thread->master, ret); else if (!__test_bit(DONT_RESPAWN_BIT, &debug)) { log_child_died("BFD", thread->u.c.pid); restart_delay = calc_restart_delay(&bfd_start_time, &bfd_next_restart_delay, "BFD"); if (!restart_delay) start_bfd_child(); else thread_add_timer(thread->master, delayed_restart_bfd_child_thread, NULL, restart_delay * TIMER_HZ); } else { log_message(LOG_ALERT, "BFD child process(%d) died: Exiting", thread->u.c.pid); raise(SIGTERM); } } #ifdef THREAD_DUMP static void register_bfd_thread_addresses(void) { register_scheduler_addresses(); register_signal_thread_addresses(); register_bfd_scheduler_addresses(); register_thread_address("bfd_dispatcher_init", bfd_dispatcher_init); register_thread_address("reload_bfd_thread", reload_bfd_thread); register_thread_address("print_bfd_thread", print_bfd_thread); register_signal_handler_address("sigreload_bfd", sigreload_bfd); register_signal_handler_address("sigdump_bfd", sigdump_bfd); register_signal_handler_address("sigend_bfd", sigend_bfd); register_signal_handler_address("thread_child_handler", thread_child_handler); #ifdef THREAD_DUMP register_signal_handler_address("thread_dump_signal", thread_dump_signal); #endif } #endif #endif int start_bfd_child(void) { #ifndef _ONE_PROCESS_DEBUG_ pid_t pid; int ret; const char *syslog_ident; /* Initialize child process */ #ifdef ENABLE_LOG_TO_FILE if (log_file_name) flush_log_file(); #endif pid = fork(); if (pid < 0) { log_message(LOG_INFO, "BFD child process: fork error(%m)"); return -1; } else if (pid) { bfd_child = pid; bfd_start_time = time_now; log_message(LOG_INFO, "Starting BFD child process, pid=%d", pid); /* Start respawning thread */ thread_add_child(master, bfd_respawn_thread, NULL, pid, TIMER_NEVER); return 0; } #ifdef _WITH_PROFILING_ /* See https://lists.gnu.org/archive/html/bug-gnu-utils/2001-09/msg00047.html for details */ monstartup ((u_long) &_start, (u_long) &etext); #endif prctl(PR_SET_PDEATHSIG, SIGTERM); /* Check our parent hasn't already changed since the fork */ if (main_pid != getppid()) kill(getpid(), SIGTERM); prog_type = PROG_TYPE_BFD; close_other_pidfiles(); /* Close the read end of the event notification pipes, and the track_process fd */ #ifdef _WITH_VRRP_ close(bfd_vrrp_event_pipe[0]); #ifdef _WITH_TRACK_PROCESS_ close_track_processes(); #endif #endif #ifdef _WITH_LVS_ close(bfd_checker_event_pipe[0]); #endif #ifdef THREAD_DUMP /* Remove anything we might have inherited from parent */ deregister_thread_addresses(); #endif initialise_debug_options(); if ((global_data->instance_name || global_data->network_namespace) && (bfd_syslog_ident = make_syslog_ident(PROG_BFD))) syslog_ident = bfd_syslog_ident; else syslog_ident = PROG_BFD; /* Opening local BFD syslog channel */ if (!__test_bit(NO_SYSLOG_BIT, &debug)) open_syslog(syslog_ident); #ifdef ENABLE_LOG_TO_FILE if (log_file_name) open_log_file(log_file_name, "bfd", global_data->network_namespace, global_data->instance_name); #endif #ifdef _MEM_CHECK_ mem_log_init(PROG_BFD, "BFD child process"); #endif free_parent_mallocs_startup(true); /* Clear any child finder functions set in parent */ set_child_finder_name(NULL); /* Create an independant file descriptor for the shared config file */ separate_config_file(); /* Child process part, write pidfile */ if (!pidfile_write(&bfd_pidfile)) { /* Fatal error */ log_message(LOG_INFO, "BFD child process: cannot write pidfile"); exit(0); } #ifdef _USE_SYSTEMD_NOTIFY_ systemd_unset_notify(); #endif /* Create the new master thread */ thread_destroy_master(master); master = thread_make_master(); /* change to / dir */ ret = chdir("/"); if (ret < 0) { log_message(LOG_INFO, "BFD child process: error chdir"); } #endif /* If last process died during a reload, we can get there and we * don't want to loop again, because we're not reloading anymore. */ UNSET_RELOAD; #ifndef _ONE_PROCESS_DEBUG_ /* Signal handling initialization */ bfd_signal_init(); /* Register emergency shutdown function */ register_shutdown_function(stop_bfd); #endif /* Start BFD daemon */ start_bfd(NULL); #ifdef _ONE_PROCESS_DEBUG_ return 0; #else #ifdef THREAD_DUMP register_bfd_thread_addresses(); #endif /* Post initializations */ #ifdef _MEM_CHECK_ log_message(LOG_INFO, "Configuration is using : %zu Bytes", get_keepalived_cur_mem_allocated()); #endif /* Launch the scheduling I/O multiplexer */ launch_thread_scheduler(master); #ifdef THREAD_DUMP deregister_thread_addresses(); #endif /* Finish BFD daemon process */ stop_bfd(EXIT_SUCCESS); /* unreachable */ exit(EXIT_SUCCESS); #endif } #ifdef THREAD_DUMP void register_bfd_parent_addresses(void) { #ifndef _ONE_PROCESS_DEBUG_ register_thread_address("bfd_respawn_thread", bfd_respawn_thread); register_thread_address("delayed_restart_bfd_child_thread", delayed_restart_bfd_child_thread); #endif } #endif keepalived-2.3.3/keepalived/bfd/bfd_parser.c0000664000175000017500000003713514412071210014433 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Configuration file parser/reader. * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #include "config.h" #include "bfd.h" #include "bfd_data.h" #include "bfd_parser.h" #include "logger.h" #include "parser.h" #include "global_parser.h" #include "utils.h" #include "global_data.h" #include "bitops.h" #ifdef _WITH_LVS_ #include "check_parser.h" #include "check_bfd.h" #endif #ifdef _WITH_VRRP_ #include "vrrp_parser.h" #include "vrrp_track.h" #include "vrrp_data.h" #endif #if defined _WITH_VRRP_ || defined _WITH_LVS_ #include "track_file.h" #endif #include "main.h" #include "assert_debug.h" static unsigned long specified_event_processes; /* Allow for English spelling */ static const char * neighbor_str = "neighbor"; static void *current_bfd; static void bfd_handler(const vector_t *strvec) { global_data->have_bfd_config = true; /* If we are not the bfd process, we don't need any more information */ if (!strvec) return; if (!(current_bfd = alloc_bfd(vector_slot(strvec, 1)))) { skip_block(true); return; } specified_event_processes = 0; } static void bfd_nbrip_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; sockaddr_t nbr_addr; assert(strvec); assert(bfd_data); if (!strcmp(vector_slot(strvec, 1), "neighbour_ip")) neighbor_str = "neighbour"; /* multihop may have already been specified */ if (inet_stosockaddr(strvec_slot(strvec, 1), bfd->multihop ? BFD_MULTIHOP_CONTROL_PORT : BFD_CONTROL_PORT, &nbr_addr)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s has" " malformed %s address %s, ignoring instance", bfd->iname, neighbor_str, strvec_slot(strvec, 1)); free_bfd(bfd); current_bfd = NULL; skip_block(false); return; } /* coverity[uninit_use] */ bfd->nbr_addr = nbr_addr; } static void bfd_srcip_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; sockaddr_t src_addr; assert(strvec); assert(bfd_data); if (inet_stosockaddr(strvec_slot(strvec, 1), NULL, &src_addr)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s has" " malformed source address %s, ignoring", bfd->iname, strvec_slot(strvec, 1)); } else { /* coverity[uninit_use] */ bfd->src_addr = src_addr; } } static void bfd_minrx_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; unsigned value; assert(strvec); assert(bfd_data); if (!read_decimal_unsigned_strvec(strvec, 1, &value, BFD_MINRX_MIN * 1000, BFD_MINRX_MAX * 1000, 3, false)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " min_rx value %s is not valid (must be in range" " [%u-%u]), ignoring", bfd->iname, strvec_slot(strvec, 1), BFD_MINRX_MIN, BFD_MINRX_MAX); return; } bfd->local_min_rx_intv = value; if (value > BFD_MINRX_MAX_SENSIBLE * 1000) log_message(LOG_INFO, "Configuration warning: BFD instance %s" " min_rx value %s is larger than max sensible (%u)", bfd->iname, strvec_slot(strvec, 1), BFD_MINRX_MAX_SENSIBLE); } static void bfd_mintx_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; unsigned value; assert(strvec); assert(bfd_data); if (!read_decimal_unsigned_strvec(strvec, 1, &value, BFD_MINTX_MIN * 1000, BFD_MINTX_MAX * 1000, 3, false)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " min_tx value %s is not valid (must be in range" " [%u-%u]), ignoring", bfd->iname, strvec_slot(strvec, 1), BFD_MINTX_MIN, BFD_MINTX_MAX); return; } bfd->local_min_tx_intv = value; if (value > BFD_MINTX_MAX_SENSIBLE * 1000) log_message(LOG_INFO, "Configuration warning: BFD instance %s" " min_tx value %s is larger than max sensible (%u)", bfd->iname, strvec_slot(strvec, 1), BFD_MINTX_MAX_SENSIBLE); } static void bfd_idletx_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; unsigned value; assert(strvec); assert(bfd_data); if (!read_decimal_unsigned_strvec(strvec, 1, &value, BFD_IDLETX_MIN * 1000, BFD_IDLETX_MAX * 1000, 3, false)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " idle_tx value %s is not valid (must be in range" " [%u-%u]), ignoring", bfd->iname, strvec_slot(strvec, 1), BFD_IDLETX_MIN, BFD_IDLETX_MAX); return; } bfd->local_idle_tx_intv = value; if (value > BFD_IDLETX_MAX_SENSIBLE * 1000) log_message(LOG_INFO, "Configuration warning: BFD instance %s" " idle_tx value %s is larger than max sensible (%u)", bfd->iname, strvec_slot(strvec, 1), BFD_IDLETX_MAX_SENSIBLE); } static void bfd_multiplier_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; unsigned value; assert(strvec); assert(bfd_data); if (!read_unsigned_strvec(strvec, 1, &value, BFD_MULTIPLIER_MIN, BFD_MULTIPLIER_MAX, false)) report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " multiplier value %s not valid (must be in range" " [%u-%u]), ignoring", bfd->iname, strvec_slot(strvec, 1), BFD_MULTIPLIER_MIN, BFD_MULTIPLIER_MAX); else bfd->local_detect_mult = value; } static void bfd_passive_handler(__attribute__((unused)) const vector_t *strvec) { bfd_t *bfd = current_bfd; assert(bfd_data); bfd->passive = true; } static void bfd_ttl_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; unsigned value; assert(strvec); assert(bfd_data); if (!read_unsigned_strvec(strvec, 1, &value, 1, BFD_TTL_MAX, false)) report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " ttl/hoplimit value %s not valid (must be in range" " [1-%d]), ignoring", bfd->iname, strvec_slot(strvec, 1), BFD_TTL_MAX); else bfd->ttl = value; } static void bfd_maxhops_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; int value; assert(strvec); assert(bfd_data); if (!read_int_strvec(strvec, 1, &value, -1, BFD_TTL_MAX, false)) report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " max_hops value %s not valid (must be in range" " [-1-%d]), ignoring", bfd->iname, strvec_slot(strvec, 1), BFD_TTL_MAX); else bfd->max_hops = value; } static void bfd_multihop_handler(const vector_t *strvec) { bfd_t *bfd = current_bfd; int value; assert(strvec); assert(bfd_data); if (vector_size(strvec) == 1) value = 1; else { value = check_true_false(vector_slot(strvec, 1)); if (value == -1) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " multihop setting not valid - %s", bfd->iname, strvec_slot(strvec, 1)); return; } } bfd->multihop = value; /* Neighbour IP may have already been specified */ #ifndef USE_SOCKADDR_STORAGE if (bfd->nbr_addr.ss_family == AF_INET) bfd->nbr_addr.in.sin_port = htons(atoi(bfd->multihop ? BFD_MULTIHOP_CONTROL_PORT : BFD_CONTROL_PORT)); else if (bfd->nbr_addr.ss_family == AF_INET6) bfd->nbr_addr.in6.sin6_port = htons(atoi(bfd->multihop ? BFD_MULTIHOP_CONTROL_PORT : BFD_CONTROL_PORT)); #else if (bfd->nbr_addr.ss_family == AF_INET) PTR_CAST(struct sockaddr_in, &bfd->nbr_addr)->sin_port = htons(atoi(bfd->multihop ? BFD_MULTIHOP_CONTROL_PORT : BFD_CONTROL_PORT)); else if (bfd->nbr_addr.ss_family == AF_INET6) PTR_CAST(struct sockaddr_in6, &bfd->nbr_addr)->sin6_port = htons(atoi(bfd->multihop ? BFD_MULTIHOP_CONTROL_PORT : BFD_CONTROL_PORT)); #endif } /* Checks for minimum configuration requirements */ #ifdef _WITH_VRRP_ static void bfd_vrrp_end_handler(void) { vrrp_tracked_bfd_t *tbfd = current_bfd; if (specified_event_processes && !__test_bit(DAEMON_VRRP, &specified_event_processes)) { free_vrrp_tracked_bfd(tbfd); return; } list_add_tail(&tbfd->e_list, &vrrp_data->vrrp_track_bfds); } #endif #ifdef _WITH_LVS_ static void bfd_checker_end_handler(void) { checker_tracked_bfd_t *cbfd = current_bfd; if (specified_event_processes && !__test_bit(DAEMON_CHECKERS, &specified_event_processes)) { free_checker_bfd(cbfd); return; } list_add_tail(&cbfd->e_list, &check_data->track_bfds); } #endif static void bfd_end_handler(void) { bfd_t *bfd = current_bfd; if (!bfd->nbr_addr.ss_family) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s has" " no %s address set, disabling instance", bfd->iname, neighbor_str); free_bfd(bfd); return; } if (bfd->src_addr.ss_family && bfd->nbr_addr.ss_family != bfd->src_addr.ss_family) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s source" " address %s and %s address %s" " are not of the same family, disabling instance", bfd->iname, inet_sockaddrtos(&bfd->src_addr), neighbor_str, inet_sockaddrtos(&bfd->nbr_addr)); free_bfd(bfd); return; } if (find_bfd_by_addr(&bfd->nbr_addr, &bfd->src_addr, bfd->multihop)) { if (bfd->src_addr.ss_family) { char src_addr[INET6_ADDRSTRLEN]; strcpy(src_addr, inet_sockaddrtos(&bfd->src_addr)); report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s has" " duplicate source/%s address %s/%s, ignoring instance", bfd->iname, neighbor_str, src_addr, inet_sockaddrtos(&bfd->nbr_addr)); } else report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s has" " duplicate %s address %s, ignoring instance", bfd->iname, neighbor_str, inet_sockaddrtos(&bfd->nbr_addr)); free_bfd(bfd); return; } if (!bfd->ttl) bfd->ttl = bfd->nbr_addr.ss_family == AF_INET ? BFD_CONTROL_TTL : BFD_CONTROL_HOPLIMIT; if (bfd->max_hops > bfd->ttl) { report_config_error(CONFIG_GENERAL_ERROR, "BFD instance %s: max_hops exceeds ttl/hoplimit - setting to ttl/hoplimit", bfd->iname); bfd->max_hops = bfd->ttl; } #ifdef _WITH_VRRP_ if (!specified_event_processes || __test_bit(DAEMON_VRRP, &specified_event_processes)) bfd->vrrp = true; #ifdef _ONE_PROCESS_DEBUG_ bfd_vrrp_end_handler(); #endif #endif #ifdef _WITH_LVS_ if (!specified_event_processes || __test_bit(DAEMON_CHECKERS, &specified_event_processes)) bfd->checker = true; #ifdef _ONE_PROCESS_DEBUG_ bfd_checker_end_handler(); #endif #endif list_add_tail(&bfd->e_list, &bfd_data->bfd); } #ifdef _WITH_VRRP_ #ifndef _ONE_PROCESS_DEBUG_ static void bfd_vrrp_handler(const vector_t *strvec) { if (!strvec) return; current_bfd = alloc_vrrp_tracked_bfd(strvec_slot(strvec, 1), &vrrp_data->vrrp_track_bfds); specified_event_processes = 0; } #endif static void bfd_vrrp_weight_handler(const vector_t *strvec) { vrrp_tracked_bfd_t *tbfd = current_bfd; int value; assert(strvec); assert(vrrp_data); if (!read_int_strvec(strvec, 1, &value, -253, 253, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " weight value %s not valid (must be in range" " [%d-%d]), ignoring", tbfd->bname, strvec_slot(strvec, 1), -253, 253); } else tbfd->weight = value; if (vector_size(strvec) >= 3) { if (strcmp(strvec_slot(strvec, 2), "reverse")) report_config_error(CONFIG_GENERAL_ERROR, "Configuration error: BFD instance %s" " unknown weight option %s", tbfd->bname, strvec_slot(strvec, 2)); else tbfd->weight_reverse = true; } } static void bfd_event_vrrp_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(DAEMON_VRRP, &specified_event_processes); } #endif #ifdef _WITH_LVS_ #ifndef _ONE_PROCESS_DEBUG_ static void bfd_checker_handler(const vector_t *strvec) { checker_tracked_bfd_t *cbfd; char *name; if (!strvec) return; name = vector_slot(strvec, 1); list_for_each_entry(cbfd, &check_data->track_bfds, e_list) { if (!strcmp(name, cbfd->bname)) { report_config_error(CONFIG_GENERAL_ERROR, "BFD %s already specified", name); skip_block(true); return; } } PMALLOC(cbfd); INIT_LIST_HEAD(&cbfd->e_list); INIT_LIST_HEAD(&cbfd->tracking_rs); cbfd->bname = STRDUP(name); current_bfd = cbfd; specified_event_processes = 0; } #endif static void bfd_event_checker_handler(__attribute__((unused)) const vector_t *strvec) { __set_bit(DAEMON_CHECKERS, &specified_event_processes); } #endif static void ignore_handler(__attribute__((unused)) const vector_t *strvec) { return; } static void install_keyword_conditional(const char *string, void (*handler) (const vector_t *), bool want_handler) { install_keyword(string, want_handler ? handler : ignore_handler); } void init_bfd_keywords(bool active) { bool bfd_handlers = false; /* This will be called with active == false for parent process, * for bfd, checker and vrrp process active will be true, but they are interested * in different keywords. */ #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_BFD || !active) #endif { install_keyword_root("bfd_instance", &bfd_handler, active, VPP ¤t_bfd); install_level_end_handler(bfd_end_handler); bfd_handlers = true; } #ifndef _ONE_PROCESS_DEBUG_ #ifdef _WITH_VRRP_ else if (prog_type == PROG_TYPE_VRRP) { install_keyword_root("bfd_instance", &bfd_vrrp_handler, active, VPP ¤t_bfd); install_level_end_handler(bfd_vrrp_end_handler); } #endif #ifdef _WITH_LVS_ else if (prog_type == PROG_TYPE_CHECKER) { install_keyword_root("bfd_instance", &bfd_checker_handler, active, VPP ¤t_bfd); install_level_end_handler(bfd_checker_end_handler); } #endif #endif install_keyword_conditional("source_ip", &bfd_srcip_handler, bfd_handlers); install_keyword_conditional("neighbor_ip", &bfd_nbrip_handler, bfd_handlers); install_keyword_conditional("neighbour_ip", &bfd_nbrip_handler, bfd_handlers); install_keyword_conditional("min_rx", &bfd_minrx_handler, bfd_handlers); install_keyword_conditional("min_tx", &bfd_mintx_handler, bfd_handlers); install_keyword_conditional("idle_tx", &bfd_idletx_handler, bfd_handlers); install_keyword_conditional("multiplier", &bfd_multiplier_handler, bfd_handlers); install_keyword_conditional("passive", &bfd_passive_handler, bfd_handlers); install_keyword_conditional("ttl", &bfd_ttl_handler, bfd_handlers); install_keyword_conditional("hoplimit", &bfd_ttl_handler, bfd_handlers); install_keyword_conditional("max_hops", &bfd_maxhops_handler, bfd_handlers); install_keyword_conditional("multihop", &bfd_multihop_handler, bfd_handlers); #ifdef _WITH_VRRP_ install_keyword_conditional("weight", &bfd_vrrp_weight_handler, #ifdef _ONE_PROCESS_DEBUG_ true #else prog_type == PROG_TYPE_VRRP #endif ); install_keyword("vrrp", &bfd_event_vrrp_handler); #endif #ifdef _WITH_LVS_ install_keyword("checker", &bfd_event_checker_handler); #endif } const vector_t * bfd_init_keywords(void) { /* global definitions mapping */ init_global_keywords(reload); init_bfd_keywords(true); #ifdef _WITH_LVS_ init_check_keywords(false); #endif #ifdef _WITH_VRRP_ init_vrrp_keywords(false); #endif #if defined _WITH_VRRP_ || defined _WITH_LVS_ add_track_file_keywords(false); #endif return keywords; } keepalived-2.3.3/keepalived/bfd/bfd.c0000664000175000017500000002140113732154322013056 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: BFD implementation as specified by RFC5880, RFC5881 * Bidirectional Forwarding Detection (BFD) is a protocol * which can provide failure detection on bidirectional path * between two hosts. A pair of host creates BFD session for * the communications path. During the communication, hosts * transmit BFD packets periodically over the path between * them, and if one host stops receiving BFD packets for * long enough, some component in the path to the correspondent * peer is assumed to have failed * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #include "config.h" #include #include #include #include "bitops.h" #include "bfd.h" #include "bfd_data.h" #include "logger.h" #include "utils.h" #include "assert_debug.h" /* Initial state */ static const bfd_t bfd0 = { .local_state = BFD_STATE_DOWN, .remote_state = BFD_STATE_DOWN, .local_discr = 0, /* ! */ .remote_discr = 0, .local_diag = BFD_DIAG_NO_DIAG, .remote_diag = BFD_DIAG_NO_DIAG, .remote_min_tx_intv = 0, .remote_min_rx_intv = 0, .local_demand = 0, .remote_demand = 0, .remote_detect_mult = 0, .poll = 0, .final = 0, .local_tx_intv = 0, .remote_tx_intv = 0, .local_detect_time = 0, .remote_detect_time = 0, .last_seen = (struct timeval) {0}, .e_list = {NULL, NULL}, /* Not used - just here to be plaisant to compiler */ }; void bfd_update_local_tx_intv(bfd_t *bfd) { bfd->local_tx_intv = bfd->local_min_tx_intv > bfd->remote_min_rx_intv ? bfd->local_min_tx_intv : bfd->remote_min_rx_intv; } void bfd_update_remote_tx_intv(bfd_t *bfd) { bfd->remote_tx_intv = bfd->local_min_rx_intv > bfd->remote_min_tx_intv ? bfd->local_min_rx_intv : bfd->remote_min_tx_intv; } void bfd_idle_local_tx_intv(bfd_t *bfd) { bfd->local_tx_intv = bfd->local_idle_tx_intv > bfd->remote_min_rx_intv ? bfd->local_idle_tx_intv : bfd->remote_min_rx_intv; } void bfd_set_poll(bfd_t *bfd) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Starting poll sequence", bfd->iname); /* * RFC5880: * ... If the timing is such that a system receiving a Poll Sequence * wishes to change the parameters described in this paragraph, the * new parameter values MAY be carried in packets with the Final (F) * bit set, even if the Poll Sequence has not yet been sent. */ if (bfd->final != 1) bfd->poll = 1; } /* Copies BFD state */ void bfd_copy_state(bfd_t *bfd, const bfd_t *bfd_old, bool all_fields) { assert(bfd_old); assert(bfd); /* Copy state variables */ bfd->local_state = bfd_old->local_state; bfd->remote_state = bfd_old->remote_state; bfd->remote_discr = bfd_old->remote_discr; bfd->remote_diag = bfd_old->remote_diag; bfd->local_demand = bfd_old->local_demand; bfd->remote_demand = bfd_old->remote_demand; bfd->poll = bfd_old->poll; bfd->final = bfd_old->final; /* * RFC5880: * When the text refers to initializing a state variable, this takes * place only at the time that the session (and the corresponding state * variables) is created. The state variables are subsequently * manipulated by the state machine and are never reinitialized, even if * the session fails and is reestablished. */ if (all_fields) { bfd->local_diag = bfd_old->local_diag; bfd->local_discr = bfd_old->local_discr; bfd->remote_min_tx_intv = bfd_old->remote_min_tx_intv; bfd->remote_min_rx_intv = bfd_old->remote_min_rx_intv; bfd->remote_detect_mult = bfd_old->remote_detect_mult; bfd->local_tx_intv = bfd_old->local_tx_intv; bfd->remote_tx_intv = bfd_old->remote_tx_intv; bfd->local_detect_time = bfd_old->local_detect_time; bfd->remote_detect_time = bfd_old->remote_detect_time; bfd->last_seen = bfd_old->last_seen; } } /* Copies thread sands */ void bfd_copy_sands(bfd_t *bfd, const bfd_t *bfd_old) { bfd->sands_out = bfd_old->sands_out; bfd->sands_exp = bfd_old->sands_exp; bfd->sands_rst = bfd_old->sands_rst; } /* Resets BFD instance to initial state */ void bfd_init_state(bfd_t *bfd) { assert(bfd); bfd_copy_state(bfd, &bfd0, true); bfd->local_discr = bfd_get_random_discr(bfd_data); bfd->local_tx_intv = bfd->local_idle_tx_intv; } void bfd_reset_state(bfd_t *bfd) { assert(bfd); bfd_copy_state(bfd, &bfd0, false); bfd_idle_local_tx_intv(bfd); } /* * Builds BFD packet */ void bfd_build_packet(bfdpkt_t *pkt, bfd_t *bfd, char *buf, const ssize_t bufsz) { ssize_t len = sizeof (bfdhdr_t); memset(buf, 0, bufsz); pkt->hdr = PTR_CAST(bfdhdr_t, buf); /* If we are responding to a poll, but also wanted * to send a poll, we can send the parameters now */ if (bfd->poll && bfd->final) bfd->poll = false; pkt->hdr->diag = bfd->local_diag; pkt->hdr->version = BFD_VERSION_1; pkt->hdr->state = bfd->local_state; pkt->hdr->poll = bfd->poll; pkt->hdr->final = bfd->final; pkt->hdr->cplane = 0; pkt->hdr->auth = 0; /* Auth is not supported */ pkt->hdr->demand = bfd->local_demand; pkt->hdr->multipoint = 0; pkt->hdr->detect_mult = bfd->local_detect_mult; pkt->hdr->len = len; pkt->hdr->local_discr = htonl(bfd->local_discr); pkt->hdr->remote_discr = htonl(bfd->remote_discr); pkt->hdr->min_tx_intv = bfd->local_state == BFD_STATE_UP ? htonl(bfd->local_min_tx_intv) : htonl(bfd->local_idle_tx_intv); pkt->hdr->min_rx_intv = htonl(bfd->local_min_rx_intv); pkt->hdr->min_echo_rx_intv = 0; /* Echo function is not supported */ pkt->len = len; pkt->dst_addr = bfd->nbr_addr; pkt->buf = buf; } /* * Performs sanity checks on a packet */ bool bfd_check_packet(const bfdpkt_t *pkt) { /* Preliminary sanity checks */ if (sizeof (bfdhdr_t) > pkt->len) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet is too small: %u bytes", pkt->len); return true; } if (pkt->hdr->len != pkt->len) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet size mismatch:" " length field: %u bytes" ", buffer size: %u bytes", pkt->hdr->len, pkt->len); return true; } /* Main Checks (RFC5880) */ if (pkt->hdr->version != BFD_VERSION_1) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet is of unsupported" " version: %d", pkt->hdr->version); return true; } if (!pkt->hdr->detect_mult) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet 'detection multiplier'" " field is zero"); return true; } if (pkt->hdr->multipoint) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet has 'multipoint' flag"); return true; } if (!pkt->hdr->local_discr) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet 'my discriminator'" " field is zero"); return true; } if (!pkt->hdr->remote_discr && pkt->hdr->state != BFD_STATE_DOWN && pkt->hdr->state != BFD_STATE_ADMINDOWN) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet 'your discriminator' field is" " zero and 'state' field is not" " Down or AdminDown"); return true; } /* Additional sanity checks */ if (pkt->hdr->poll && pkt->hdr->final) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet has both poll and final" " flags set"); return true; } if (!BFD_VALID_DIAG(pkt->hdr->diag)) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet has invalid 'diag'" " field: %d", pkt->hdr->diag); return true; } return false; } bool bfd_check_packet_ttl(const bfdpkt_t *pkt, const bfd_t *bfd) { /* Generalized TTL Security Mechanism Check (RFC5881) * - extended so we can specify a maximum number of hops */ if (pkt->ttl && bfd->max_hops + pkt->ttl < bfd->ttl) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Packet %s(%u) < %d - discarding", pkt->src_addr.ss_family == AF_INET ? "ttl" : "hop_limit", pkt->ttl, bfd->ttl - bfd->max_hops); return true; } return false; } keepalived-2.3.3/keepalived/bfd/Makefile.in0000664000175000017500000004464414772274255014257 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2017-2017 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/bfd ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libbfd_a_AR = $(AR) $(ARFLAGS) libbfd_a_DEPENDENCIES = am_libbfd_a_OBJECTS = bfd.$(OBJEXT) bfd_data.$(OBJEXT) \ bfd_parser.$(OBJEXT) bfd_daemon.$(OBJEXT) \ bfd_scheduler.$(OBJEXT) bfd_event.$(OBJEXT) libbfd_a_OBJECTS = $(am_libbfd_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/lib depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/bfd.Po ./$(DEPDIR)/bfd_daemon.Po \ ./$(DEPDIR)/bfd_data.Po ./$(DEPDIR)/bfd_event.Po \ ./$(DEPDIR)/bfd_parser.Po ./$(DEPDIR)/bfd_scheduler.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libbfd_a_SOURCES) $(EXTRA_libbfd_a_SOURCES) DIST_SOURCES = $(libbfd_a_SOURCES) $(EXTRA_libbfd_a_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib \ $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libbfd.a libbfd_a_SOURCES = \ bfd.c bfd_data.c bfd_parser.c bfd_daemon.c bfd_scheduler.c \ bfd_event.c EXTRA_libbfd_a_SOURCES = libbfd_a_LIBADD = all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/bfd/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/bfd/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libbfd.a: $(libbfd_a_OBJECTS) $(libbfd_a_DEPENDENCIES) $(EXTRA_libbfd_a_DEPENDENCIES) $(AM_V_at)-rm -f libbfd.a $(AM_V_AR)$(libbfd_a_AR) libbfd.a $(libbfd_a_OBJECTS) $(libbfd_a_LIBADD) $(AM_V_at)$(RANLIB) libbfd.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bfd.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bfd_daemon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bfd_data.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bfd_event.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bfd_parser.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bfd_scheduler.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/bfd.Po -rm -f ./$(DEPDIR)/bfd_daemon.Po -rm -f ./$(DEPDIR)/bfd_data.Po -rm -f ./$(DEPDIR)/bfd_event.Po -rm -f ./$(DEPDIR)/bfd_parser.Po -rm -f ./$(DEPDIR)/bfd_scheduler.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/bfd.Po -rm -f ./$(DEPDIR)/bfd_daemon.Po -rm -f ./$(DEPDIR)/bfd_data.Po -rm -f ./$(DEPDIR)/bfd_event.Po -rm -f ./$(DEPDIR)/bfd_parser.Po -rm -f ./$(DEPDIR)/bfd_scheduler.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-generic clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/bfd/bfd_scheduler.c0000664000175000017500000010373714412066323015130 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Scheduling framework for bfd code * * Author: Ilya Voronin, * * 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) 2015-2017 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include #include #include #include "bfd.h" #include "bfd_data.h" #include "bfd_scheduler.h" #include "bfd_event.h" #include "parser.h" #include "logger.h" #include "memory.h" #include "main.h" #include "bitops.h" #include "utils.h" #include "signals.h" #include "assert_debug.h" /* Locals */ static int bfd_send_packet(int, bfdpkt_t *, bool); static void bfd_sender_schedule(bfd_t *); static int bfd_open_fd_out(bfd_t *); static void bfd_state_down(bfd_t *, uint8_t diag); /* * Session sender thread * * Runs every local_tx_intv, or after reception of a packet * with Poll bit set */ inline static unsigned long thread_time_to_wakeup(thread_ref_t thread) { struct timeval tmp_time; /* Make sure thread->sands is not in the past */ if (thread->sands.tv_sec < time_now.tv_sec || (thread->sands.tv_sec == time_now.tv_sec && thread->sands.tv_usec <= time_now.tv_usec)) return 1; timersub(&thread->sands, &time_now, &tmp_time); return timer_long(tmp_time); } /* Sends one BFD control packet and reschedules itself if needed */ static void bfd_sender_thread(thread_ref_t thread) { bfd_t *bfd; bfdpkt_t pkt; assert(thread); bfd = THREAD_ARG(thread); assert(bfd); assert(!BFD_ISADMINDOWN(bfd)); if (thread->type != THREAD_EVENT) bfd->thread_out = NULL; bfd_build_packet(&pkt, bfd, bfd_buffer, BFD_BUFFER_SIZE); if (bfd_send_packet(bfd->fd_out, &pkt, !bfd->send_error) == -1) { if (!bfd->send_error) { log_message(LOG_ERR, "(%s) Error sending packet", bfd->iname); bfd->send_error = true; } } else bfd->send_error = false; /* Reset final flag if set */ bfd->final = 0; /* Schedule next run if not called as an event thread */ if (thread->type != THREAD_EVENT) bfd_sender_schedule(bfd); } /* Schedules bfd_sender_thread to run in local_tx_intv minus applied jitter */ static uint32_t get_jitter(bfd_t * bfd) { uint32_t min_jitter; /* * RFC5880: * The periodic transmission of BFD Control packets MUST be jittered * on a per-packet basis by up to 25%, that is, the interval MUST be * reduced by a random value of 0 to 25% <...> * * If bfd.DetectMult is equal to 1, the interval between transmitted * BFD Control packets MUST be no more than 90% of the negotiated * transmission interval, and MUST be no less than 75% of the * negotiated transmission interval. */ if (bfd->local_detect_mult) min_jitter = bfd->local_tx_intv / 10; /* 10% <=> / 10 */ else min_jitter = 0; return rand_intv(min_jitter, bfd->local_tx_intv / 4); /* 25% <=> / 4 */ } /* Schedules bfd_sender_thread to run in local_tx_intv minus applied jitter */ static void bfd_sender_schedule(bfd_t *bfd) { assert(bfd); assert(!bfd->thread_out); bfd->thread_out = thread_add_timer(master, bfd_sender_thread, bfd, bfd->local_tx_intv - get_jitter(bfd)); } /* Cancels bfd_sender_thread run */ static void bfd_sender_cancel(bfd_t *bfd) { assert(bfd); assert(bfd->thread_out); thread_cancel(bfd->thread_out); bfd->thread_out = NULL; } /* Reschedules bfd_sender_thread run (usually after local_tx_intv change) */ static void bfd_sender_reschedule(bfd_t *bfd) { assert(bfd); assert(bfd->thread_out); timer_thread_update_timeout(bfd->thread_out, bfd->local_tx_intv - get_jitter(bfd)); } /* Returns 1 if bfd_sender_thread is scheduled to run, 0 otherwise */ static int __attribute__ ((pure)) bfd_sender_scheduled(bfd_t *bfd) { assert(bfd); return bfd->thread_out != NULL; } /* Suspends sender thread. Needs freshly updated time_now */ static void bfd_sender_suspend(bfd_t * bfd) { assert(bfd); assert(bfd->thread_out); assert(bfd->sands_out == TIMER_NEVER); bfd->sands_out = thread_time_to_wakeup(bfd->thread_out); bfd_sender_cancel(bfd); } /* Resumes sender thread */ static void bfd_sender_resume(bfd_t *bfd) { assert(bfd); assert(!bfd->thread_out); assert(bfd->sands_out != TIMER_NEVER); if (!bfd->passive || bfd->local_state == BFD_STATE_UP) bfd->thread_out = thread_add_timer(master, bfd_sender_thread, bfd, bfd->sands_out); bfd->sands_out = TIMER_NEVER; } /* Returns 1 if bfd_sender_thread is suspended, 0 otherwise */ static int __attribute__ ((pure)) bfd_sender_suspended(bfd_t *bfd) { assert(bfd); return bfd->sands_out != TIMER_NEVER; } static void bfd_sender_discard(bfd_t *bfd) { assert(bfd); assert(bfd->sands_out != TIMER_NEVER); bfd->sands_out = TIMER_NEVER; } /* * Session expiration thread * * Runs after local_detect_time has passed since receipt of last * BFD control packet from neighbor */ /* Marks session as down because of Control Detection Time Expiration */ static void bfd_expire_thread(thread_ref_t thread) { bfd_t *bfd; uint32_t dead_time, overdue_time; timeval_t dead_time_tv; assert(thread); bfd = THREAD_ARG(thread); assert(bfd); /* Session cannot expire while not in Up or Init states */ assert(BFD_ISUP(bfd) || BFD_ISINIT(bfd)); bfd->thread_exp = NULL; /* Time since last received control packet */ timersub(&time_now, &bfd->last_seen, &dead_time_tv); dead_time = timer_long(dead_time_tv); /* Difference between expected and actual failure detection time */ overdue_time = dead_time - bfd->local_detect_time; if (bfd->local_state == BFD_STATE_UP || __test_bit(LOG_EXTRA_DETAIL_BIT, &debug)) log_message(LOG_WARNING, "(%s) Expired after" " %" PRIu32 " ms (%" PRIu32 " usec overdue)", bfd->iname, dead_time / 1000, overdue_time); /* * RFC5880: * <...> If a period of a Detection Time passes without the * receipt of a valid, authenticated BFD packet from the remote * system, this variable MUST be set to zero. */ bfd->remote_discr = 0; bfd_state_down(bfd, BFD_DIAG_EXPIRED); } /* Schedules bfd_expire_thread to run in local_detect_time */ static void bfd_expire_schedule(bfd_t *bfd) { assert(bfd); assert(!bfd->thread_exp); bfd->thread_exp = thread_add_timer(master, bfd_expire_thread, bfd, bfd->local_detect_time); } /* Cancels bfd_expire_thread run */ static void bfd_expire_cancel(bfd_t *bfd) { assert(bfd); assert(bfd->thread_exp); thread_cancel(bfd->thread_exp); bfd->thread_exp = NULL; } /* Reschedules bfd_expire_thread run (usually after control packet receipt) */ static void bfd_expire_reschedule(bfd_t *bfd) { assert(bfd); assert(bfd->thread_exp); timer_thread_update_timeout(bfd->thread_exp, bfd->local_detect_time); } /* Returns 1 if bfd_expire_thread is scheduled to run, 0 otherwise */ static int __attribute__ ((pure)) bfd_expire_scheduled(bfd_t *bfd) { assert(bfd); return bfd->thread_exp != NULL; } /* Suspends expire thread. Needs freshly updated time_now */ static void bfd_expire_suspend(bfd_t *bfd) { assert(bfd); assert(bfd->thread_exp); assert(bfd->sands_exp == TIMER_NEVER); bfd->sands_exp = thread_time_to_wakeup(bfd->thread_exp); bfd_expire_cancel(bfd); } /* Resumes expire thread */ static void bfd_expire_resume(bfd_t *bfd) { assert(bfd); assert(!bfd->thread_exp); assert(bfd->sands_exp != TIMER_NEVER); bfd->thread_exp = thread_add_timer(master, bfd_expire_thread, bfd, bfd->sands_exp); bfd->sands_exp = TIMER_NEVER; } /* Returns 1 if bfd_expire_thread is suspended, 0 otherwise */ static int __attribute__ ((pure)) bfd_expire_suspended(bfd_t *bfd) { assert(bfd); return bfd->sands_exp != TIMER_NEVER; } static void bfd_expire_discard(bfd_t *bfd) { assert(bfd); assert(bfd->sands_exp != TIMER_NEVER); bfd->sands_exp = TIMER_NEVER; } /* * Session reset thread * * Runs after local_detect_time has passed after BFD session * gone to Down state. */ /* Resets BFD session to initial state */ static void bfd_reset_thread(thread_ref_t thread) { bfd_t *bfd; assert(thread); bfd = THREAD_ARG(thread); assert(bfd); assert(bfd->thread_rst); bfd->thread_rst = NULL; bfd_reset_state(bfd); } /* Schedules bfd_reset_thread to run in local_detect_time */ static void bfd_reset_schedule(bfd_t * bfd) { assert(bfd); assert(!bfd->thread_rst); bfd->thread_rst = thread_add_timer(master, bfd_reset_thread, bfd, bfd->local_detect_time); } /* Cancels bfd_reset_thread run */ static void bfd_reset_cancel(bfd_t *bfd) { assert(bfd); assert(bfd->thread_rst); thread_cancel(bfd->thread_rst); bfd->thread_rst = NULL; } /* Returns 1 if bfd_reset_thread is scheduled to run, 0 otherwise */ static int __attribute__ ((pure)) bfd_reset_scheduled(bfd_t *bfd) { assert(bfd); return bfd->thread_rst != NULL; } /* Suspends reset thread. Needs freshly updated time_now */ static void bfd_reset_suspend(bfd_t *bfd) { assert(bfd); assert(bfd->thread_rst); assert(bfd->sands_rst == TIMER_NEVER); bfd->sands_rst = thread_time_to_wakeup(bfd->thread_rst); bfd_reset_cancel(bfd); } /* Resumes reset thread */ static void bfd_reset_resume(bfd_t *bfd) { assert(bfd); assert(!bfd->thread_rst); assert(bfd->sands_rst != TIMER_NEVER); bfd->thread_rst = thread_add_timer(master, bfd_reset_thread, bfd, bfd->sands_rst); bfd->sands_rst = TIMER_NEVER; } /* Returns 1 if bfd_reset_thread is suspended, 0 otherwise */ static int __attribute__ ((pure)) bfd_reset_suspended(bfd_t *bfd) { assert(bfd); return bfd->sands_rst != TIMER_NEVER; } static void bfd_reset_discard(bfd_t *bfd) { assert(bfd); assert(bfd->sands_rst != TIMER_NEVER); bfd->sands_rst = TIMER_NEVER; } /* Cancels bfd_open_fd_out_thread run */ static void bfd_open_fd_out_cancel(bfd_t *bfd) { assert(bfd); assert(bfd->thread_open_fd_out); thread_cancel(bfd->thread_open_fd_out); bfd->thread_open_fd_out = NULL; } /* Returns 1 if bfd_open_fd_out_thread is scheduled to run, 0 otherwise */ static int __attribute__ ((pure)) bfd_open_fd_out_scheduled(bfd_t *bfd) { assert(bfd); return bfd->thread_open_fd_out != NULL; } /* * State change handlers */ /* Common actions for Down and AdminDown states */ static void bfd_state_fall(bfd_t *bfd, bool send_event) { assert(bfd); /* * RFC5880: * When bfd.SessionState is not Up, the system MUST set * bfd.DesiredMinTxInterval to a value of not less than * one second (1,000,000 microseconds) */ bfd_idle_local_tx_intv(bfd); if (bfd_expire_scheduled(bfd)) bfd_expire_cancel(bfd); if (send_event && bfd->remote_state != BFD_STATE_ADMINDOWN) bfd_event_send(bfd); } /* Runs when BFD session state goes Down */ static void bfd_state_down(bfd_t *bfd, uint8_t diag) { assert(bfd); assert(BFD_VALID_DIAG(diag)); int old_state = bfd->local_state; if (bfd->local_state == BFD_STATE_UP) bfd->local_discr = bfd_get_random_discr(bfd_data); if (bfd->local_state == BFD_STATE_UP || __test_bit(LOG_EXTRA_DETAIL_BIT, &debug)) log_message(LOG_WARNING, "(%s) Entering %s state" " (Local diagnostic - %s, Remote diagnostic - %s)", bfd->iname, BFD_STATE_STR(BFD_STATE_DOWN), BFD_DIAG_STR(diag), BFD_DIAG_STR(bfd->remote_diag)); bfd->local_state = BFD_STATE_DOWN; bfd->local_diag = diag; bfd_reset_schedule(bfd); if (bfd->passive && bfd_sender_scheduled(bfd)) bfd_sender_cancel(bfd); bfd_state_fall(bfd, old_state == BFD_STATE_UP); } /* Runs when BFD session state goes AdminDown */ static void bfd_state_admindown(bfd_t *bfd) { assert(bfd); bfd->local_state = BFD_STATE_ADMINDOWN; bfd->local_diag = BFD_DIAG_ADMIN_DOWN; if (bfd_sender_scheduled(bfd)) bfd_sender_cancel(bfd); log_message(LOG_WARNING, "(%s) Entering %s state", bfd->iname, BFD_STATE_STR(bfd->local_state)); bfd_state_fall(bfd, false); } /* Common actions for Init and Up states */ static void bfd_state_rise(bfd_t *bfd) { /* RFC5880 doesn't state if this must be done or not */ bfd->local_diag = BFD_DIAG_NO_DIAG; if (bfd->local_state == BFD_STATE_UP || __test_bit(LOG_EXTRA_DETAIL_BIT, &debug)) log_message(LOG_INFO, "(%s) Entering %s state", bfd->iname, BFD_STATE_STR(bfd->local_state)); if (bfd_reset_scheduled(bfd)) bfd_reset_cancel(bfd); if (!bfd_expire_scheduled(bfd)) bfd_expire_schedule(bfd); } /* Runs when BFD session state goes Up */ static void bfd_state_up(bfd_t *bfd) { assert(bfd); bfd->local_state = BFD_STATE_UP; bfd_state_rise(bfd); if (bfd->local_idle_tx_intv != bfd->local_min_tx_intv) bfd_set_poll(bfd); bfd_event_send(bfd); } /* Runs when BFD session state goes Init */ static void bfd_state_init(bfd_t *bfd) { assert(bfd); /* According to RFC5880 session cannot directly transition from Init to Up state */ assert(!BFD_ISUP(bfd)); bfd->local_state = BFD_STATE_INIT; bfd_state_rise(bfd); if (bfd->passive && !bfd_sender_scheduled(bfd)) bfd_sender_schedule(bfd); } /* Dumps current timers values */ static void bfd_dump_timers(FILE *fp, bfd_t *bfd) { int indent; assert(bfd); indent = 2 + strlen(bfd->iname); conf_write(fp, "(%s)" " ---------------< Session parameters >--------------", bfd->iname); conf_write(fp, "%*s" " min_tx min_rx tx_intv mult detect_time", indent, ""); conf_write(fp, "%*s" " local %8u %7u %8u %5u %12" PRIu64, indent, "", (bfd->local_state == BFD_STATE_UP ? bfd->local_min_tx_intv : bfd->local_idle_tx_intv), bfd->local_min_rx_intv, bfd->local_tx_intv, bfd->local_detect_mult, bfd->local_detect_time); conf_write(fp, "%*s" " remote %8u %7u %8u %5u %12" PRIu64, indent, "", bfd->remote_min_tx_intv, bfd->remote_min_rx_intv, bfd->remote_tx_intv, bfd->remote_detect_mult, bfd->remote_detect_time); } /* * Packet handling functions */ /* Sends a control packet to the neighbor (called from bfd_sender_thread) returns -1 on error */ static int bfd_send_packet(int fd, bfdpkt_t *pkt, bool log_error) { int ret; socklen_t dstlen; assert(fd >= 0); assert(pkt); if (pkt->dst_addr.ss_family == AF_INET) dstlen = sizeof (struct sockaddr_in); else dstlen = sizeof (struct sockaddr_in6); ret = sendto(fd, pkt->buf, pkt->len, 0, PTR_CAST(struct sockaddr, &pkt->dst_addr), dstlen); if (ret == -1 && log_error) log_message(LOG_ERR, "sendto() error (%m)"); return ret; } /* Handles incoming control packet (called from bfd_receiver_thread) and processes it through a BFD state machine. */ static void bfd_handle_packet(bfdpkt_t *pkt, bool multihop) { uint32_t old_local_tx_intv; uint32_t old_remote_rx_intv; uint32_t old_remote_tx_intv; uint8_t old_remote_detect_mult; uint64_t old_local_detect_time; bfd_t *bfd; assert(pkt); assert(pkt->hdr); /* Perform sanity checks on a packet */ if (bfd_check_packet(pkt)) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Discarding bogus packet from %s", inet_sockaddrtopair(&pkt->src_addr)); return; } /* Lookup session */ if (!pkt->hdr->remote_discr) bfd = find_bfd_by_addr(&pkt->src_addr, &pkt->dst_addr, multihop); else bfd = find_bfd_by_discr(ntohl(pkt->hdr->remote_discr)); if (!bfd) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Discarding packet from %s" " (session is not found - your" " discriminator field is 0x%8.8x)", inet_sockaddrtopair(&pkt->src_addr), ntohl(pkt->hdr->remote_discr)); return; } /* We can't check the TTL any earlier, since we need to know what * is configured for this particular instance */ if (bfd->max_hops != UCHAR_MAX && bfd_check_packet_ttl(pkt, bfd)) return; /* Authentication is not supported for now */ if (pkt->hdr->auth != 0) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_ERR, "Discarding packet from %s" " (auth bit is set, but no authentication" " is in use)", inet_sockaddrtopair(&pkt->src_addr)); return; } /* Discard all packets while in AdminDown state */ if (bfd->local_state == BFD_STATE_ADMINDOWN) { /* See if we can open the out socket */ if (bfd_open_fd_out(bfd)) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Discarding packet from %s" " (session is in AdminDown state)", inet_sockaddrtopair(&pkt->src_addr)); return; } /* Open was successful, cancel the thread to open the socket */ if (bfd_open_fd_out_scheduled(bfd)) bfd_open_fd_out_cancel(bfd); bfd_state_init(bfd); } /* Save old timers */ old_remote_rx_intv = bfd->remote_min_rx_intv; old_remote_tx_intv = bfd->remote_min_tx_intv; old_remote_detect_mult = bfd->remote_detect_mult; old_local_detect_time = bfd->local_detect_time ; old_local_tx_intv = bfd->local_tx_intv ; /* Update state variables */ bfd->remote_discr = ntohl(pkt->hdr->local_discr); bfd->remote_state = pkt->hdr->state; bfd->remote_diag = pkt->hdr->diag; bfd->remote_min_rx_intv = ntohl(pkt->hdr->min_rx_intv); bfd->remote_min_tx_intv = ntohl(pkt->hdr->min_tx_intv); bfd->remote_demand = pkt->hdr->demand; bfd->remote_detect_mult = pkt->hdr->detect_mult; /* Terminate poll sequence */ if (pkt->hdr->final) bfd->poll = 0; /* * Recalculate local and remote TX intervals if: * Control packet with 'Final' bit is received OR * Control packet with 'Poll' bit is received OR * Session is not UP */ if ((bfd->local_state == BFD_STATE_UP && (pkt->hdr->poll || pkt->hdr->final)) || bfd->local_state != BFD_STATE_UP) { if (bfd->remote_state == BFD_STATE_UP && (bfd->local_state == BFD_STATE_INIT || bfd->local_state == BFD_STATE_UP)) bfd_update_local_tx_intv(bfd); else bfd_idle_local_tx_intv(bfd); bfd_update_remote_tx_intv(bfd); } /* Update the Detection Time */ bfd->local_detect_time = bfd->remote_detect_mult * bfd->remote_tx_intv; bfd->remote_detect_time = bfd->local_detect_mult * bfd->local_tx_intv; /* Check if timers are changed */ if (__test_bit(LOG_EXTRA_DETAIL_BIT, &debug) || (__test_bit(LOG_DETAIL_BIT, &debug) && (bfd->remote_min_rx_intv != old_remote_rx_intv || bfd->remote_min_tx_intv != old_remote_tx_intv || bfd->remote_detect_mult != old_remote_detect_mult || bfd->local_tx_intv != old_local_tx_intv))) bfd_dump_timers(NULL, bfd); /* Reschedule sender if local_tx_intv is being reduced */ if (bfd->local_tx_intv < old_local_tx_intv && bfd_sender_scheduled(bfd)) bfd_sender_reschedule(bfd); /* Report detection time changes */ if (bfd->local_detect_time != old_local_detect_time) { int len = strlen(bfd->iname) + 2; log_message(LOG_INFO, "%*s Detection time" " is %" PRIu64 " us (was %" PRIu64 " us)", len, "", bfd->local_detect_time, old_local_detect_time); } /* BFD state machine */ if (bfd->remote_state == BFD_STATE_ADMINDOWN && bfd->local_state != BFD_STATE_DOWN) bfd_state_down(bfd, BFD_DIAG_NBR_SIGNALLED_DOWN); else { if (bfd->local_state == BFD_STATE_DOWN) { if (bfd->remote_state == BFD_STATE_DOWN) bfd_state_init(bfd); else if (bfd->remote_state == BFD_STATE_INIT) bfd_state_up(bfd); } else if (bfd->local_state == BFD_STATE_INIT) { if (bfd->remote_state == BFD_STATE_INIT || bfd->remote_state == BFD_STATE_UP) bfd_state_up(bfd); } else if (bfd->local_state == BFD_STATE_UP) if (bfd->remote_state == BFD_STATE_DOWN) bfd_state_down(bfd, BFD_DIAG_NBR_SIGNALLED_DOWN); } if (bfd->remote_demand && bfd->local_state == BFD_STATE_UP && bfd->remote_state == BFD_STATE_UP) if (bfd_sender_scheduled(bfd)) bfd_sender_cancel(bfd); if (!bfd->remote_demand || bfd->local_state != BFD_STATE_UP || bfd->remote_state != BFD_STATE_UP) if (!bfd_sender_scheduled(bfd)) bfd_sender_schedule(bfd); if (pkt->hdr->poll) { bfd->final = 1; thread_add_event(master, bfd_sender_thread, bfd, 0); } /* Update last seen timer */ bfd->last_seen = timer_now(); /* Delay expiration if scheduled */ if (bfd->local_state == BFD_STATE_UP && bfd_expire_scheduled(bfd)) bfd_expire_reschedule(bfd); } /* Reads one packet from input socket */ static int bfd_receive_packet(bfdpkt_t *pkt, int fd, char *buf, ssize_t bufsz) { ssize_t len; unsigned int ttl = 0; struct msghdr msg; struct cmsghdr *cmsg = NULL; char cbuf[CMSG_SPACE(max(sizeof(struct in6_pktinfo), sizeof(struct in_pktinfo)) + CMSG_SPACE(sizeof(ttl)))] __attribute__((aligned(__alignof__(struct cmsghdr)))); struct iovec iov[1]; const struct in6_pktinfo *pktinfo; assert(pkt); assert(fd >= 0); assert(buf); assert(bufsz); iov[0].iov_base = buf; iov[0].iov_len = bufsz; msg.msg_name = &pkt->src_addr; msg.msg_namelen = sizeof (pkt->src_addr); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = cbuf; msg.msg_controllen = sizeof (cbuf); msg.msg_flags = 0; /* Unnecessary, but keep coverity happy */ len = recvmsg(fd, &msg, MSG_DONTWAIT); if (len == -1) { log_message(LOG_ERR, "recvmsg() error (%m)"); return 1; } if (msg.msg_flags & MSG_TRUNC) { log_message(LOG_WARNING, "recvmsg() message truncated"); return 1; } if (msg.msg_flags & MSG_CTRUNC) log_message(LOG_WARNING, "recvmsg() control message truncated"); for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) || (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT)) ttl = *CMSG_DATA(cmsg); else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { pktinfo = PTR_CAST_CONST(struct in6_pktinfo, CMSG_DATA(cmsg)); if (IN6_IS_ADDR_V4MAPPED(&pktinfo->ipi6_addr)) { PTR_CAST(struct sockaddr_in, &pkt->dst_addr)->sin_addr.s_addr = pktinfo->ipi6_addr.s6_addr32[3]; pkt->dst_addr.ss_family = AF_INET; } else { memcpy(&PTR_CAST(struct sockaddr_in6, &pkt->dst_addr)->sin6_addr, &pktinfo->ipi6_addr, sizeof(pktinfo->ipi6_addr)); pkt->dst_addr.ss_family = AF_INET6; } } else if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { PTR_CAST(struct sockaddr_in, &pkt->dst_addr)->sin_addr = PTR_CAST(struct in_pktinfo, CMSG_DATA(cmsg))->ipi_addr; pkt->dst_addr.ss_family = AF_INET; } else log_message(LOG_WARNING, "recvmsg() received" " unexpected control message (level %d type %d)", cmsg->cmsg_level, cmsg->cmsg_type); } if (!ttl) log_message(LOG_WARNING, "recvmsg() returned no TTL control message"); pkt->hdr = PTR_CAST(bfdhdr_t, buf); pkt->len = len; pkt->ttl = ttl; /* Convert an IPv4-mapped IPv6 address to a real IPv4 address */ if (pkt->src_addr.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&PTR_CAST(struct sockaddr_in6, &pkt->src_addr)->sin6_addr)) { PTR_CAST(struct sockaddr_in, &pkt->src_addr)->sin_addr.s_addr = PTR_CAST(struct sockaddr_in6, &pkt->src_addr)->sin6_addr.s6_addr32[3]; pkt->src_addr.ss_family = AF_INET; } return 0; } /* * Reciever thread */ /* Runs when data is available in listening socket */ static void bfd_receiver_thread(thread_ref_t thread) { bfd_data_t *data; bfdpkt_t pkt; int fd; assert(thread); data = THREAD_ARG(thread); assert(data); fd = thread->u.f.fd; assert(fd >= 0); data->thread_in = NULL; /* Ignore THREAD_READ_TIMEOUT */ if (thread->type == THREAD_READY_READ_FD) { if (!bfd_receive_packet(&pkt, fd, bfd_buffer, BFD_BUFFER_SIZE)) bfd_handle_packet(&pkt, data->multihop_fd_in == fd); } data->thread_in = thread_add_read(thread->master, bfd_receiver_thread, data, fd, TIMER_NEVER, 0); } /* * Initialization functions */ /* Prepares UDP socket for listening on *:3784 or *:4784 (multihop) - both IPv4 and IPv6 */ static int bfd_open_fd_in(const char *port) { struct addrinfo hints = { .ai_family = AF_INET6, .ai_flags = AI_NUMERICSERV | AI_PASSIVE, .ai_protocol = IPPROTO_UDP, .ai_socktype = SOCK_DGRAM }; struct addrinfo *ai_in; int ret; int yes = 1; int sav_errno; int fd_in; if ((ret = getaddrinfo(NULL, port, &hints, &ai_in))) { log_message(LOG_ERR, "getaddrinfo() error %d (%s)", ret, gai_strerror(ret)); return -1; } if ((fd_in = socket(AF_INET6, ai_in->ai_socktype, ai_in->ai_protocol)) == -1) { sav_errno = errno; freeaddrinfo(ai_in); if (sav_errno != EAFNOSUPPORT) { log_message(LOG_ERR, "socket() error %d (%m)", errno); return -1; } /* IPv6 is disabled on the system, so we need to try using IPv4 */ hints.ai_family = AF_INET; if ((ret = getaddrinfo(NULL, port, &hints, &ai_in))) { log_message(LOG_ERR, "getaddrinfo(AF_INET) error %d (%s)", ret, gai_strerror(ret)); return -1; } if ((fd_in = socket(AF_INET, ai_in->ai_socktype, ai_in->ai_protocol)) == -1) { log_message(LOG_ERR, "socket(AF_INET) error %d (%m)", errno); freeaddrinfo(ai_in); return -1; } } if ((ret = setsockopt(fd_in, IPPROTO_IP, IP_RECVTTL, &yes, sizeof (yes))) == -1) log_message(LOG_ERR, "setsockopt(IP_RECVTTL) error %d (%m)", errno); else if (ai_in->ai_family == AF_INET6 && (ret = setsockopt(fd_in, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof (yes))) == -1) log_message(LOG_ERR, "setsockopt(IPV6_RECVHOPLIMIT) error %d (%m)", errno); else if (ai_in->ai_family == AF_INET6 && (ret = setsockopt(fd_in, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof (yes))) == -1) log_message(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO) error %d (%m)", errno); else if (ai_in->ai_family == AF_INET && (ret = setsockopt(fd_in, IPPROTO_IP, IP_PKTINFO, &yes, sizeof (yes))) == -1) log_message(LOG_ERR, "setsockopt(IP_PKTINFO) error %d (%m)", errno); else if ((ret = bind(fd_in, ai_in->ai_addr, ai_in->ai_addrlen)) == -1) log_message(LOG_ERR, "bind() error %d (%m)", errno); freeaddrinfo(ai_in); if (ret) { close(fd_in); return -1; } return fd_in; } static bool read_local_port_range(uint32_t port_limits[2]) { char buf[5 + 1 + 5 + 1 + 1]; /* 3276860999 */ int fd; ssize_t len; long val[2]; char *endptr; /* Default to sensible values */ port_limits[0] = 49152; port_limits[1] = 60999; fd = open("/proc/sys/net/ipv4/ip_local_port_range", O_RDONLY); if (fd == -1) return false; len = read(fd, buf, sizeof(buf)); close(fd); if (len == -1 || len == sizeof(buf)) return false; buf[len] = '\0'; val[0] = strtol(buf, &endptr, 10); if (val[0] <= 0 || val[0] == LONG_MAX || (*endptr != '\t' && *endptr != ' ')) return false; val[1] = strtol(endptr + 1, &endptr, 10); if (val[1] <= 0 || val[1] == LONG_MAX || *endptr != '\n') return false; port_limits[0] = val[0]; port_limits[1] = val[1]; return true; } /* Prepares UDP socket for sending data to neighbor */ static int bfd_open_fd_out(bfd_t *bfd) { int ttl; int ret; uint32_t port_limits[2]; uint16_t orig_port, port; socklen_t sockaddr_len; assert(bfd); assert(bfd->fd_out == -1); bfd->fd_out = socket(bfd->nbr_addr.ss_family, SOCK_DGRAM, IPPROTO_UDP); if (bfd->fd_out == -1) { log_message(LOG_ERR, "(%s) socket() error (%m)", bfd->iname); return 1; } if (bfd->src_addr.ss_family) { /* Generate a random port number within the valid range */ read_local_port_range(port_limits); if (port_limits[0] < BFD_MIN_PORT) port_limits[0] = BFD_MIN_PORT; if (port_limits[1] > BFD_MAX_PORT) port_limits[1] = BFD_MAX_PORT; /* Ensure we have a range of at least 1024 ports (an arbitrary number) * to try. */ if (port_limits[0] + 1023 > port_limits[1]) { /* Just use the BFD defaults */ port_limits[0] = BFD_MIN_PORT; port_limits[1] = BFD_MAX_PORT; } orig_port = port = rand_intv(port_limits[0], port_limits[1]); sockaddr_len = bfd->src_addr.ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); do { /* Try binding socket to the address until we find one available */ if (bfd->src_addr.ss_family == AF_INET) PTR_CAST(struct sockaddr_in, &bfd->src_addr)->sin_port = htons(port); else PTR_CAST(struct sockaddr_in6, &bfd->src_addr)->sin6_port = htons(port); ret = bind(bfd->fd_out, PTR_CAST(struct sockaddr, &bfd->src_addr), sockaddr_len); if (ret == -1 && errno == EADDRINUSE) { /* Port already in use, try next */ if (++port > port_limits[1]) port = port_limits[0]; if (port == orig_port) break; continue; } break; } while (true); if (ret == -1) { log_message(LOG_ERR, "(%s) bind() error (%m)", bfd->iname); close(bfd->fd_out); bfd->fd_out = -1; return 1; } } else { /* We have a problem here - we do not have a source address, and so * cannot bind the socket. That means that we will get a system allocated * port, which may be outside the range [49152, 65535], as specified in * RFC5881. */ } ttl = bfd->ttl; if (bfd->nbr_addr.ss_family == AF_INET) ret = setsockopt(bfd->fd_out, IPPROTO_IP, IP_TTL, &ttl, sizeof (ttl)); else ret = setsockopt(bfd->fd_out, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof (ttl)); if (ret == -1) { log_message(LOG_ERR, "(%s) setsockopt() error (%m)", bfd->iname); close(bfd->fd_out); bfd->fd_out = -1; return 1; } return 0; } /* Opens all needed sockets */ static int bfd_open_fds(bfd_data_t *data) { bfd_t *bfd; bool need_multihop = false; bool need_singlehop = false; assert(data); list_for_each_entry(bfd, &data->bfd, e_list) { if (bfd_open_fd_out(bfd)) { log_message(LOG_ERR, "(%s) Unable to" " open output socket, disabling instance", bfd->iname); bfd_state_admindown(bfd); } else { if (bfd->multihop) need_multihop = true; else need_singlehop = true; } } /* Do not reopen input socket(s) alreay open, on reload. * On reload, close unneeded open sockets. */ if (data->fd_in == -1) { if (need_singlehop && (data->fd_in = bfd_open_fd_in(BFD_CONTROL_PORT)) == -1) { log_message(LOG_ERR, "Unable to open listening socket"); /* There is no point to stay alive w/o listening socket */ if (!need_multihop) return 1; } } else if (!need_singlehop) { close(data->fd_in); data->fd_in = -1; } if (data->multihop_fd_in == -1) { if (need_multihop && (data->multihop_fd_in = bfd_open_fd_in(BFD_MULTIHOP_CONTROL_PORT)) == -1) { log_message(LOG_ERR, "Unable to open multihop listening socket"); /* There is no point to stay alive w/o listening socket */ if (!need_singlehop) return 1; } } else if (!need_multihop) { close(data->multihop_fd_in); data->multihop_fd_in = -1; } return 0; } static void bfd_open_fd_out_thread(thread_ref_t thread) { bfd_t *bfd = THREAD_ARG(thread); if (bfd->fd_out != -1) return; if (bfd_open_fd_out(bfd)) { bfd->thread_open_fd_out = thread_add_timer(master, bfd_open_fd_out_thread, bfd, 60 * TIMER_HZ); return; } bfd->local_state = BFD_STATE_DOWN; bfd->sands_out = TIMER_NEVER; if (!bfd->passive) bfd_sender_schedule(bfd); } /* Registers sender and receiver threads */ static void bfd_register_workers(bfd_data_t *data) { bfd_t *bfd; assert(data); assert(!data->thread_in); /* Set timeout to not expire */ if (data->fd_in != -1) data->thread_in = thread_add_read(master, bfd_receiver_thread, data, data->fd_in, TIMER_NEVER, 0); if (data->multihop_fd_in != -1) data->thread_in = thread_add_read(master, bfd_receiver_thread, data, data->multihop_fd_in, TIMER_NEVER, 0); /* Resume or schedule threads */ list_for_each_entry(bfd, &data->bfd, e_list) { /* Do not start anything if instance is in AdminDown state. Discard saved state if any */ if (bfd_sender_suspended(bfd)) { if (BFD_ISADMINDOWN(bfd)) bfd_sender_discard(bfd); else bfd_sender_resume(bfd); } else if (!BFD_ISADMINDOWN(bfd) && !bfd->passive) bfd_sender_schedule(bfd); if (bfd_expire_suspended(bfd)) { if (BFD_ISADMINDOWN(bfd)) bfd_expire_discard(bfd); else bfd_expire_resume(bfd); } if (bfd_reset_suspended(bfd)) { if (BFD_ISADMINDOWN(bfd)) bfd_reset_discard(bfd); else bfd_reset_resume(bfd); } /* Send our status to VRRP process */ bfd_event_send(bfd); if (BFD_ISADMINDOWN(bfd) && bfd->fd_out == -1) bfd->thread_open_fd_out = thread_add_timer(master, bfd_open_fd_out_thread, bfd, 60 * TIMER_HZ); /* If we are starting up, send a packet */ if (!reload && !bfd->passive && !BFD_ISADMINDOWN(bfd)) thread_add_event(master, bfd_sender_thread, bfd, 0); } } /* Suspends threads, closes sockets */ void bfd_dispatcher_release(bfd_data_t *data) { bfd_t *bfd; assert(data); /* Looks like dispatcher wasn't initialized yet This can happen is case of a configuration error */ if (!data->thread_in) return; thread_cancel(data->thread_in); data->thread_in = NULL; /* Do not close fd_in(s) on reload */ if (!reload) { if (data->fd_in != -1) { close(data->fd_in); data->fd_in = -1; } if (data->multihop_fd_in != -1) { close(data->multihop_fd_in); data->multihop_fd_in = -1; } } /* Suspend threads for possible resuming after reconfiguration */ set_time_now(); list_for_each_entry(bfd, &data->bfd, e_list) { if (bfd_sender_scheduled(bfd)) bfd_sender_suspend(bfd); if (bfd_expire_scheduled(bfd)) bfd_expire_suspend(bfd); if (bfd_reset_scheduled(bfd)) bfd_reset_suspend(bfd); if (bfd_open_fd_out_scheduled(bfd)) bfd_open_fd_out_cancel(bfd); if (bfd->fd_out != -1) { close(bfd->fd_out); bfd->fd_out = -1; } } cancel_signal_read_thread(); } /* Starts BFD dispatcher */ void bfd_dispatcher_init(thread_ref_t thread) { bfd_data_t *data; assert(thread); data = THREAD_ARG(thread); if (bfd_open_fds(data)) exit(EXIT_FAILURE); bfd_register_workers(data); } #ifdef THREAD_DUMP void register_bfd_scheduler_addresses(void) { register_thread_address("bfd_sender_thread", bfd_sender_thread); register_thread_address("bfd_expire_thread", bfd_expire_thread); register_thread_address("bfd_reset_thread", bfd_reset_thread); register_thread_address("bfd_receiver_thread", bfd_receiver_thread); register_thread_address("bfd_open_fd_out_thread", bfd_open_fd_out_thread); } #endif keepalived-2.3.3/keepalived/etc/0000775000175000017500000000000014772274313012271 5keepalived-2.3.3/keepalived/etc/keepalived/0000775000175000017500000000000014772274313014402 5keepalived-2.3.3/keepalived/etc/keepalived/keepalived.conf.sample.in0000664000175000017500000000705114502300564021157 ! Configuration File for keepalived global_defs { notification_email { acassen@firewall.loc failover@firewall.loc sysadmin@firewall.loc } notification_email_from Alexandre.Cassen@firewall.loc smtp_server 192.168.200.1 smtp_connect_timeout 30 router_id LVS_DEVEL vrrp_skip_check_adv_addr vrrp_strict vrrp_garp_interval 0 vrrp_gna_interval 0 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.200.16 192.168.200.17 192.168.200.18 } # Allow packets addressed to the VIPs above to be received accept } virtual_server 192.168.200.100 443 { delay_loop 6 lb_algo rr lb_kind NAT persistence_timeout 50 protocol TCP real_server 192.168.201.100 443 { weight 1 SSL_GET { url { path / digest ff20ad2481f97b1754ef3e12ecd3a9cc } url { path /mrtg/ digest 9b3a0c85a887a256d6939da88aabd8cd } connect_timeout 3 retry 3 delay_before_retry 3 } } } virtual_server 10.10.10.2 1358 { delay_loop 6 lb_algo rr lb_kind NAT persistence_timeout 50 protocol TCP sorry_server 192.168.200.200 1358 real_server 192.168.200.2 1358 { weight 1 HTTP_GET { url { path /testurl/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } url { path /testurl2/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } url { path /testurl3/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } connect_timeout 3 retry 3 delay_before_retry 3 } } real_server 192.168.200.3 1358 { weight 1 HTTP_GET { url { path /testurl/test.jsp digest 640205b7b0fc66c1ea91c463fac6334c } url { path /testurl2/test.jsp digest 640205b7b0fc66c1ea91c463fac6334c } connect_timeout 3 retry 3 delay_before_retry 3 } } } virtual_server 10.10.10.3 1358 { delay_loop 3 lb_algo rr lb_kind NAT persistence_timeout 50 protocol TCP real_server 192.168.200.4 1358 { weight 1 HTTP_GET { url { path /testurl/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } url { path /testurl2/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } url { path /testurl3/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } connect_timeout 3 retry 3 delay_before_retry 3 } } real_server 192.168.200.5 1358 { weight 1 HTTP_GET { url { path /testurl/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } url { path /testurl2/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } url { path /testurl3/test.jsp digest 640205b7b0fc66c1ea91c463fac6334d } connect_timeout 3 retry 3 delay_before_retry 3 } } } keepalived-2.3.3/keepalived/etc/keepalived/Makefile.am0000664000175000017500000000457614165530217016364 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2020-2020 Alexandre Cassen, cp = echo " CP $@"; \ cp @DEFAULT_CONFIG_FILENAME@.sample: $(srcdir)/keepalived.conf.sample.in @$(cp) '$<' $@ EXTRA_DIST = keepalived.conf.sample.in configdir = @DEFAULT_CONFIG_DIR@ config_DATA = @DEFAULT_CONFIG_FILENAME@.sample MOSTLYCLEANFILES = @DEFAULT_CONFIG_FILENAME@.sample # If $DESTDIR is not blank, the following changes are not done since it is assumed to be a staged # installation. This means it will not apply to distros building keepalived. # After keepalived v2.2.4 we stopped installing a default keepalived.conf and instead install # keepalived.conf.sample. We also fixed a bug in keepalived where it always used /etc/keepalived.conf # rather than $(configdir)/keepalived.conf as the default configuration file. # The following checks if $(configdir)/keepalived.conf exists and if it matches one of the sample # config files (any version since keepalived v0.6.6) (the series of sed commands edit older versions # to match the current version), and if it matches, simply removes the config file (since it can't # be used to run keepalived), then if $configdir is not /etc copies /etc/keepalived.conf to $configdir # and updates /etc/keepalived.conf with a comment at the beginning to state that the configuration # file has been moved to $configdir. # The code below incorporates $(DESTDIR) to facilitate testing if the test -z $(DESTDIR) is changed to # test -n $(DESTDIR). install-data-hook: @if test -z "$(DESTDIR)" && test -f $(DESTDIR)$(configdir)/@DEFAULT_CONFIG_FILENAME@; then \ tr "\n" "~" <$(DESTDIR)$(configdir)/@DEFAULT_CONFIG_FILENAME@ | \ @SED@ -e "s/\(10.10.10.2 1358 {~ delay_loop 6~ lb_algo rr ~ lb_kind NAT~\) nat_mask 255.255.255.0~/\1/" | \ @SED@ -e "s/lvs_id /router_id /" | \ @SED@ -e "s/~[^~]*nat_mask[^~]*~/~/g" | \ @SED@ -e "s/\(~ router_id LVS_DEVEL~\)}/\1 vrrp_skip_check_adv_addr~}/" | \ @SED@ -e "s/\(~ vrrp_skip_check_adv_addr~\)}/\1 vrrp_strict~}/" | \ @SED@ -e "s/\(~ vrrp_strict~\)}/\1 vrrp_garp_interval 0~ vrrp_gna_interval 0~}/" | \ @SED@ -e "s/ ~/~/g" | \ @SED@ -e "s/nb_get_retry/retry/g" | \ tr "~" "\n" | \ diff -q - $(DESTDIR)$(configdir)/@DEFAULT_CONFIG_FILENAME@.sample; \ if test $$? -eq 0; then \ rm $(DESTDIR)$(configdir)/@DEFAULT_CONFIG_FILENAME@; \ fi; \ fi; keepalived-2.3.3/keepalived/etc/keepalived/Makefile.in0000664000175000017500000004162414772274255016403 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2020-2020 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/etc/keepalived ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(configdir)" DATA = $(config_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ cp = echo " CP $@"; \ cp EXTRA_DIST = keepalived.conf.sample.in configdir = @DEFAULT_CONFIG_DIR@ config_DATA = @DEFAULT_CONFIG_FILENAME@.sample MOSTLYCLEANFILES = @DEFAULT_CONFIG_FILENAME@.sample all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/etc/keepalived/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/etc/keepalived/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-configDATA: $(config_DATA) @$(NORMAL_INSTALL) @list='$(config_DATA)'; test -n "$(configdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(configdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(configdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(configdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(configdir)" || exit $$?; \ done uninstall-configDATA: @$(NORMAL_UNINSTALL) @list='$(config_DATA)'; test -n "$(configdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(configdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(configdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-configDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-configDATA .MAKE: install-am install-data-am install-strip .PHONY: all all-am check check-am clean clean-generic cscopelist-am \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-configDATA \ install-data install-data-am install-data-hook install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-configDATA .PRECIOUS: Makefile @DEFAULT_CONFIG_FILENAME@.sample: $(srcdir)/keepalived.conf.sample.in @$(cp) '$<' $@ # If $DESTDIR is not blank, the following changes are not done since it is assumed to be a staged # installation. This means it will not apply to distros building keepalived. # After keepalived v2.2.4 we stopped installing a default keepalived.conf and instead install # keepalived.conf.sample. We also fixed a bug in keepalived where it always used /etc/keepalived.conf # rather than $(configdir)/keepalived.conf as the default configuration file. # The following checks if $(configdir)/keepalived.conf exists and if it matches one of the sample # config files (any version since keepalived v0.6.6) (the series of sed commands edit older versions # to match the current version), and if it matches, simply removes the config file (since it can't # be used to run keepalived), then if $configdir is not /etc copies /etc/keepalived.conf to $configdir # and updates /etc/keepalived.conf with a comment at the beginning to state that the configuration # file has been moved to $configdir. # The code below incorporates $(DESTDIR) to facilitate testing if the test -z $(DESTDIR) is changed to # test -n $(DESTDIR). install-data-hook: @if test -z "$(DESTDIR)" && test -f $(DESTDIR)$(configdir)/@DEFAULT_CONFIG_FILENAME@; then \ tr "\n" "~" <$(DESTDIR)$(configdir)/@DEFAULT_CONFIG_FILENAME@ | \ @SED@ -e "s/\(10.10.10.2 1358 {~ delay_loop 6~ lb_algo rr ~ lb_kind NAT~\) nat_mask 255.255.255.0~/\1/" | \ @SED@ -e "s/lvs_id /router_id /" | \ @SED@ -e "s/~[^~]*nat_mask[^~]*~/~/g" | \ @SED@ -e "s/\(~ router_id LVS_DEVEL~\)}/\1 vrrp_skip_check_adv_addr~}/" | \ @SED@ -e "s/\(~ vrrp_skip_check_adv_addr~\)}/\1 vrrp_strict~}/" | \ @SED@ -e "s/\(~ vrrp_strict~\)}/\1 vrrp_garp_interval 0~ vrrp_gna_interval 0~}/" | \ @SED@ -e "s/ ~/~/g" | \ @SED@ -e "s/nb_get_retry/retry/g" | \ tr "~" "\n" | \ diff -q - $(DESTDIR)$(configdir)/@DEFAULT_CONFIG_FILENAME@.sample; \ if test $$? -eq 0; then \ rm $(DESTDIR)$(configdir)/@DEFAULT_CONFIG_FILENAME@; \ fi; \ fi; # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/etc/openrc/0000775000175000017500000000000014105735173013553 5keepalived-2.3.3/keepalived/etc/openrc/keepalived0000664000175000017500000000022613050370667015530 #!/sbin/openrc-run command="/sbin/$SVCNAME" command_args="$KEEPALIVED_OPTS" pidfile="/var/run/$SVCNAME.pid" depend() { need net after firewall } keepalived-2.3.3/keepalived/etc/sysconfig/0000775000175000017500000000000014772274311014273 5keepalived-2.3.3/keepalived/etc/sysconfig/.gitignore0000664000175000017500000000001314020263665016171 keepalived keepalived-2.3.3/keepalived/etc/sysconfig/keepalived.in0000664000175000017500000000126514020263665016654 # Options for keepalived. See `keepalived --help' output and keepalived(8) and # keepalived.conf(5) man pages for a list of all options. Here are the most # common ones : # # --vrrp -P Only run with VRRP subsystem. # --check -C Only run with Health-checker subsystem. # --dont-release-vrrp -V Dont remove VRRP VIPs & VROUTEs on daemon stop. # --dont-release-ipvs -I Dont remove IPVS topology on daemon stop. # --dump-conf -d Dump the configuration data. # --log-detail -D Detailed log messages. # --log-facility -S 0-7 Set local syslog facility (default=LOG_DAEMON) # KEEPALIVED_OPTIONS="@KEEPALIVED_RUNTIME_OPTIONS@" keepalived-2.3.3/keepalived/etc/sysconfig/Makefile.am0000664000175000017500000000072314227040314016236 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2021-2021 Alexandre Cassen, edit = echo " EDIT $@"; \ @SED@ \ -e 's|@KEEPALIVED_RUNTIME_OPTIONS[@]|$(KEEPALIVED_RUNTIME_OPTIONS)|g' keepalived: $(srcdir)/keepalived.in $(builddir)/Makefile @rm -f $@ $@.tmp @$(edit) '$(srcdir)/$@.in' >$@ EXTRA_DIST = keepalived.in sysconfigdir = $(sysconfdir)/sysconfig sysconfig_DATA = keepalived MOSTLYCLEANFILES = keepalived keepalived-2.3.3/keepalived/etc/sysconfig/Makefile0000664000175000017500000004066014772274311015661 # Makefile.in generated by automake 1.16.5 from Makefile.am. # keepalived/etc/sysconfig/Makefile. Generated from Makefile.in by configure. # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2021-2021 Alexandre Cassen, am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/keepalived pkgincludedir = $(includedir)/keepalived pkglibdir = $(libdir)/keepalived pkglibexecdir = $(libexecdir)/keepalived am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/etc/sysconfig ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_$(V)) am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY)) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_$(V)) am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_$(V)) am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(sysconfigdir)" DATA = $(sysconfig_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = ${SHELL} '/home/acassen/src/keepalived/build-aux/missing' aclocal-1.16 AMTAR = $${TAR-tar} AM_DEFAULT_VERBOSITY = 0 AR = ar ARFLAGS = cr AUTOCONF = ${SHELL} '/home/acassen/src/keepalived/build-aux/missing' autoconf AUTOHEADER = ${SHELL} '/home/acassen/src/keepalived/build-aux/missing' autoheader AUTOMAKE = ${SHELL} '/home/acassen/src/keepalived/build-aux/missing' automake-1.16 AWK = mawk CC = gcc CCDEPMODE = depmode=gcc3 CFLAGS = -g -O2 CPPFLAGS = -D_GNU_SOURCE CSCOPE = cscope CTAGS = ctags CYGPATH_W = echo DBUS_DATADIR = /usr/local/share DEFAULT_CONFIG_DIR = /etc/keepalived DEFAULT_CONFIG_FILE = /etc/keepalived/keepalived.conf DEFAULT_CONFIG_FILENAME = keepalived.conf DEFS = -DHAVE_CONFIG_H DEPDIR = .deps ECHO_C = ECHO_N = -n ECHO_T = ETAGS = etags EXEEXT = EXPANDED_DATADIR = /usr/local/share GREP = /usr/bin/grep HAVE_RPM = No HAVE_RPMBUILD = HAVE_SPHINX_BUILD = No INSTALL = /usr/bin/install -c INSTALL_DATA = ${INSTALL} -m 644 INSTALL_PROGRAM = ${INSTALL} INSTALL_SCRIPT = ${INSTALL} INSTALL_STRIP_PROGRAM = $(install_sh) -c -s KA_CFLAGS = -g -g -O2 -Wall -Wextra -Wunused -Wstrict-prototypes -Wabsolute-value -Waddress-of-packed-member -Walloca -Walloc-zero -Warith-conversion -Warray-bounds=2 -Wattribute-alias=2 -Wbad-function-cast -Wc11-c2x-compat -Wcast-align -Wcast-qual -Wdate-time -Wdisabled-optimization -Wdouble-promotion -Wduplicated-branches -Wduplicated-cond -Wfloat-conversion -Wfloat-equal -Wformat-overflow -Wformat-security -Wformat-signedness -Wformat-truncation -Wframe-larger-than=5120 -Wimplicit-fallthrough=3 -Winit-self -Winline -Winvalid-pch -Wjump-misses-init -Wlogical-op -Wmissing-declarations -Wmissing-field-initializers -Wmissing-include-dirs -Wmissing-prototypes -Wnested-externs -Wnormalized -Wnull-dereference -Wold-style-definition -Woverlength-strings -Wpointer-arith -Wredundant-decls -Wshadow -Wshift-overflow=2 -Wstack-protector -Wstrict-overflow=4 -Wstringop-overflow=2 -Wstringop-truncation -Wsuggest-attribute=cold -Wsuggest-attribute=format -Wsuggest-attribute=malloc -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure -Wsync-nand -Wtrampolines -Wundef -Wuninitialized -Wunknown-pragmas -Wunsafe-loop-optimizations -Wunsuffixed-float-constants -Wunused-const-variable=2 -Wvariadic-macros -Wwrite-strings -fno-strict-aliasing -fPIE -Wformat -Werror=format-security -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -O2 KA_CPPFLAGS = -D_GNU_SOURCE KA_LDFLAGS = -pie -Wl,-z,relro -Wl,-z,now KA_LIBS = -lm -lssl -lcrypto KA_TMP_DIR = /tmp KEEPALIVED_CONFIG_OPTIONS = KEEPALIVED_RUNTIME_OPTIONS = -D LDD = ldd LDFLAGS = LIBOBJS = LIBS = LN_S = ln -s LTLIBOBJS = MAINTAINERCLEANFILES = *~ *.orig *.rej core core.* MAKEINFO = ${SHELL} '/home/acassen/src/keepalived/build-aux/missing' makeinfo MKDIR_P = /usr/bin/mkdir -p NETSNMP_CONFIG = OBJEXT = o OLD_DEFAULT_CONFIG_FILE = PACKAGE = keepalived PACKAGE_BUGREPORT = keepalived-users@groups.io PACKAGE_NAME = Keepalived PACKAGE_STRING = Keepalived 2.3.3 PACKAGE_TARNAME = keepalived PACKAGE_URL = http://www.keepalived.org/ PACKAGE_VERSION = 2.3.3 PATH_SEPARATOR = : PKG_CONFIG = /usr/bin/pkg-config PKG_CONFIG_LIBDIR = PKG_CONFIG_PATH = RANLIB = ranlib RUNSTATEDIR = /run SAMPLES_DIR = ${sysconfdir}/keepalived/samples SED = /usr/bin/sed SET_MAKE = SHELL = /bin/bash SNMP_SERVICE = SPHINXBUILDNAME = sphinx-build STRIP = strip SYSTEMD_EXEC_START_OPTIONS = SYSTEMD_SERVICE_TYPE = forking USE_LLD = VERSION = 2.3.3 abs_builddir = /home/acassen/src/keepalived/keepalived/etc/sysconfig abs_srcdir = /home/acassen/src/keepalived/keepalived/etc/sysconfig abs_top_builddir = /home/acassen/src/keepalived abs_top_srcdir = /home/acassen/src/keepalived ac_ct_AR = ar ac_ct_CC = gcc am__include = include am__leading_dot = . am__quote = am__tar = $${TAR-tar} chof - "$$tardir" am__untar = $${TAR-tar} xf - bindir = ${exec_prefix}/bin build_alias = builddir = . datadir = ${datarootdir} datarootdir = ${prefix}/share docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} dvidir = ${docdir} exec_prefix = ${prefix} host_alias = htmldir = ${docdir} includedir = ${prefix}/include infodir = ${datarootdir}/info install_sh = ${SHELL} /home/acassen/src/keepalived/build-aux/install-sh libdir = ${exec_prefix}/lib libexecdir = ${exec_prefix}/libexec localedir = ${datarootdir}/locale localstatedir = ${prefix}/var mandir = ${datarootdir}/man mkdir_p = $(MKDIR_P) oldincludedir = /usr/include pdfdir = ${docdir} prefix = /usr/local program_transform_name = s,x,x, psdir = ${docdir} runstatedir = ${localstatedir}/run sbindir = ${exec_prefix}/sbin sharedstatedir = ${prefix}/com srcdir = . sysconfdir = ${prefix}/etc systemdsystemunitdir = /usr/lib/systemd/system target_alias = top_build_prefix = ../../../ top_builddir = ../../.. top_srcdir = ../../.. edit = echo " EDIT $@"; \ /usr/bin/sed \ -e 's|@KEEPALIVED_RUNTIME_OPTIONS[@]|$(KEEPALIVED_RUNTIME_OPTIONS)|g' EXTRA_DIST = keepalived.in sysconfigdir = $(sysconfdir)/sysconfig sysconfig_DATA = keepalived MOSTLYCLEANFILES = keepalived all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/etc/sysconfig/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/etc/sysconfig/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-sysconfigDATA: $(sysconfig_DATA) @$(NORMAL_INSTALL) @list='$(sysconfig_DATA)'; test -n "$(sysconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sysconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sysconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(sysconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(sysconfigdir)" || exit $$?; \ done uninstall-sysconfigDATA: @$(NORMAL_UNINSTALL) @list='$(sysconfig_DATA)'; test -n "$(sysconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(sysconfigdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(sysconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-sysconfigDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-sysconfigDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic cscopelist-am \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip install-sysconfigDATA \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags-am uninstall uninstall-am \ uninstall-sysconfigDATA .PRECIOUS: Makefile keepalived: $(srcdir)/keepalived.in $(builddir)/Makefile @rm -f $@ $@.tmp @$(edit) '$(srcdir)/$@.in' >$@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/etc/sysconfig/Makefile.in0000664000175000017500000003567614772274255016310 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2021-2021 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/etc/sysconfig ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(sysconfigdir)" DATA = $(sysconfig_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ edit = echo " EDIT $@"; \ @SED@ \ -e 's|@KEEPALIVED_RUNTIME_OPTIONS[@]|$(KEEPALIVED_RUNTIME_OPTIONS)|g' EXTRA_DIST = keepalived.in sysconfigdir = $(sysconfdir)/sysconfig sysconfig_DATA = keepalived MOSTLYCLEANFILES = keepalived all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/etc/sysconfig/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/etc/sysconfig/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-sysconfigDATA: $(sysconfig_DATA) @$(NORMAL_INSTALL) @list='$(sysconfig_DATA)'; test -n "$(sysconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sysconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sysconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(sysconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(sysconfigdir)" || exit $$?; \ done uninstall-sysconfigDATA: @$(NORMAL_UNINSTALL) @list='$(sysconfig_DATA)'; test -n "$(sysconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(sysconfigdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(sysconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-sysconfigDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-sysconfigDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic cscopelist-am \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip install-sysconfigDATA \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags-am uninstall uninstall-am \ uninstall-sysconfigDATA .PRECIOUS: Makefile keepalived: $(srcdir)/keepalived.in $(builddir)/Makefile @rm -f $@ $@.tmp @$(edit) '$(srcdir)/$@.in' >$@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/etc/Makefile.am0000664000175000017500000000027714020263665014245 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2021 Alexandre Cassen, EXTRA_DIST = openrc sysconfig SUBDIRS = init init.d keepalived sysconfig keepalived-2.3.3/keepalived/etc/init.d/0000775000175000017500000000000014772274313013456 5keepalived-2.3.3/keepalived/etc/init.d/keepalived.suse.init.in0000664000175000017500000000733413220013273017745 #! /bin/sh ### BEGIN INIT INFO # Provides: keepalived # Required-Start: $remote_fs $syslog # Required-Stop : $remote_fs $syslog # Default-Start : 3 5 # Default-Stop : 0 1 2 6 # Description : Start keepalived to allow XY and provide YZ # continued on second line by '#' ### END INIT INFO DAEMON="Keepalived daemon" DAEMON_BIN="@sbindir@/keepalived" DAEMON_CONF="@sysconfdir@/keepalived/keepalived.conf" DAEMON_PIDFILE="@localstatedir@/run/keepalived.pid" DAEMON_OPT="-d" #DAEMON_USER="root" SUPPORTS_HUP="yes" # PidFile @localstatedir@/run/keepalived.pid # DatabaseOwner root pid_par=${DAEMON_PIDFILE:+"-p $DAEMON_PIDFILE"} usr_par=${DAEMON_USER:+"-u $DAEMON_USER"} test -x $DAEMON_BIN || exit 5 # Shell functions sourced from /etc/rc.status: # rc_check check and set local and overall rc status # rc_status check and set local and overall rc status # rc_status -v ditto but be verbose in local rc status # rc_status -v -r ditto and clear the local rc status # rc_failed set local and overall rc status to failed # rc_failed set local and overall rc status to # rc_reset clear local rc status (overall remains) # rc_exit exit appropriate to overall rc status . /etc/rc.status # First reset status of this service rc_reset # Return values acc. to LSB for all commands but status: # 0 - success # 1 - generic or unspecified error # 2 - invalid or excess argument(s) # 3 - unimplemented feature (e.g. "reload") # 4 - insufficient privilege # 5 - program is not installed # 6 - program is not configured # 7 - program is not running # # Note that starting an already running service, stopping # or restarting a not-running service as well as the restart # with force-reload (in case signalling is not supported) are # considered a success. # remove empty pid files to avoid disturbing warnings by checkproc/killproc # (these can occur if dhcpd does not start correctly) test -e $DAEMON_PIDFILE && ! test -s $DAEMON_PIDFILE && rm $DAEMON_PIDFILE case "$1" in start) echo -n "Starting $DAEMON " if test ! -f ${DAEMON_CONF}; then echo -n >&2 "Configuration file, ${DAEMON_CONF} does not exist. " rc_status -s exit 6 fi checkproc $pid_par ${DAEMON_BIN} case $? in 0) echo -n "- Warning: daemon already running. " ;; 1) echo -n "- Warning: ${DAEMON_PIDFILE} exists. " ;; esac # echo "startproc $usr_par $pid_par ${DAEMON_BIN} ${DAEMON_OPT}" startproc $usr_par $pid_par ${DAEMON_BIN} ${DAEMON_OPT} rc_status -v ;; stop) echo -n "Shutting down $DAEMON " checkproc $pid_par ${DAEMON_BIN} || \ echo -n " Warning: daemon not running. " killproc $pid_par -t 10 ${DAEMON_BIN} rc_status -v ;; try-restart|condrestart) if test "$1" = "condrestart"; then echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" fi $0 status if test $? = 0; then $0 restart else rc_reset fi rc_status ;; restart) $0 stop $0 start rc_status ;; force-reload|reload) if test "$SUPPORTS_HUP" = "yes"; then echo -n "Reload service $DAEMON " checkproc $pid_par ${DAEMON_BIN} && \ touch ${DAEMON_PIDFILE} || \ echo -n >&2 " Warning: daemon not running. " killproc $pid_par -HUP ${DAEMON_BIN} rc_status -v else $0 stop && sleep 3 && $0 start rc_status fi ;; status) echo -n "Checking for $DAEMON " checkproc $pid_par ${DAEMON_BIN} rc_status -v ;; probe) test ${DAEMON_CONF} -nt ${DAEMON_PIDFILE} && echo reload ;; *) echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" exit 1 ;; esac rc_exit keepalived-2.3.3/keepalived/etc/init.d/keepalived0000775000175000017500000000243412766347655015453 #!/bin/sh # # Startup script for the Keepalived daemon # # processname: keepalived # pidfile: /var/run/keepalived.pid # config: /etc/keepalived/keepalived.conf # chkconfig: - 21 79 # description: Start and stop Keepalived # Source function library . /etc/rc.d/init.d/functions # Source configuration file (we set KEEPALIVED_OPTIONS there) . /etc/sysconfig/keepalived RETVAL=0 prog="keepalived" start() { echo -n $"Starting $prog: " daemon keepalived ${KEEPALIVED_OPTIONS} RETVAL=$? echo [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog } stop() { echo -n $"Stopping $prog: " killproc keepalived RETVAL=$? echo [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog } reload() { echo -n $"Reloading $prog: " killproc keepalived -1 RETVAL=$? echo } # See how we were called. case "$1" in start) start ;; stop) stop ;; reload) reload ;; restart) stop start ;; condrestart) if [ -f /var/lock/subsys/$prog ]; then stop start fi ;; status) status keepalived RETVAL=$? ;; *) echo "Usage: $0 {start|stop|reload|restart|condrestart|status}" RETVAL=1 esac exit $RETVAL keepalived-2.3.3/keepalived/etc/init.d/Makefile.am0000664000175000017500000000130414227040314015413 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, EXTRA_DIST = keepalived.suse.init.in keepalived edit = echo " EDIT $@"; \ @SED@ \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' if INIT_SUSE keepalived.suse.init: $(builddir)/Makefile @rm -f $@ $@.tmp @$(edit) '$(srcdir)/$@.in' >$@ keepalived.suse.init: $(srcdir)/keepalived.suse.init.in endif if INIT_SYSV initdir = $(sysconfdir)/rc.d/init.d init_DATA = keepalived endif if INIT_SUSE susedir = $(sysconfdir)/init suse_DATA = keepalived.suse.init endif MOSTLYCLEANFILES = keepalived.suse.init keepalived-2.3.3/keepalived/etc/init.d/Makefile.in0000664000175000017500000003777614772274255015474 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/etc/init.d ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(initdir)" "$(DESTDIR)$(susedir)" DATA = $(init_DATA) $(suse_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = keepalived.suse.init.in keepalived edit = echo " EDIT $@"; \ @SED@ \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' @INIT_SYSV_TRUE@initdir = $(sysconfdir)/rc.d/init.d @INIT_SYSV_TRUE@init_DATA = keepalived @INIT_SUSE_TRUE@susedir = $(sysconfdir)/init @INIT_SUSE_TRUE@suse_DATA = keepalived.suse.init MOSTLYCLEANFILES = keepalived.suse.init all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/etc/init.d/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/etc/init.d/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-initDATA: $(init_DATA) @$(NORMAL_INSTALL) @list='$(init_DATA)'; test -n "$(initdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(initdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(initdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(initdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(initdir)" || exit $$?; \ done uninstall-initDATA: @$(NORMAL_UNINSTALL) @list='$(init_DATA)'; test -n "$(initdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(initdir)'; $(am__uninstall_files_from_dir) install-suseDATA: $(suse_DATA) @$(NORMAL_INSTALL) @list='$(suse_DATA)'; test -n "$(susedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(susedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(susedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(susedir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(susedir)" || exit $$?; \ done uninstall-suseDATA: @$(NORMAL_UNINSTALL) @list='$(suse_DATA)'; test -n "$(susedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(susedir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(initdir)" "$(DESTDIR)$(susedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-initDATA install-suseDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-initDATA uninstall-suseDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic cscopelist-am \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-initDATA install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ install-suseDATA installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic pdf pdf-am ps ps-am tags-am uninstall \ uninstall-am uninstall-initDATA uninstall-suseDATA .PRECIOUS: Makefile @INIT_SUSE_TRUE@keepalived.suse.init: $(builddir)/Makefile @INIT_SUSE_TRUE@ @rm -f $@ $@.tmp @INIT_SUSE_TRUE@ @$(edit) '$(srcdir)/$@.in' >$@ @INIT_SUSE_TRUE@keepalived.suse.init: $(srcdir)/keepalived.suse.init.in # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/etc/Makefile.in0000664000175000017500000004510714772274255014272 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2021 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/etc ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = openrc sysconfig SUBDIRS = init init.d keepalived sysconfig all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/etc/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/etc/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-recursive clean-am: clean-generic mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic cscopelist-am ctags ctags-am \ distclean distclean-generic distclean-tags distdir dvi dvi-am \ html html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/etc/init/0000775000175000017500000000000014772274313013234 5keepalived-2.3.3/keepalived/etc/init/keepalived.conf.in0000664000175000017500000000035413052261272016531 description "load-balancing and high-availability service" start on runlevel [2345] stop on runlevel [!2345] respawn script . @sysconfdir@/sysconfig/keepalived exec @sbindir@/keepalived --dont-fork ${KEEPALIVED_OPTIONS} end script keepalived-2.3.3/keepalived/etc/init/Makefile.am0000664000175000017500000000101114227040314015164 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, EXTRA_DIST = keepalived.conf.in edit = echo " EDIT $@"; \ @SED@ \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' keepalived.conf: $(builddir)/Makefile @rm -f $@ $@.tmp @$(edit) '$(srcdir)/$@.in' >$@ keepalived.conf: $(srcdir)/keepalived.conf.in if INIT_UPSTART initdir = $(sysconfdir)/init init_DATA = keepalived.conf endif MOSTLYCLEANFILES = keepalived.conf keepalived-2.3.3/keepalived/etc/init/Makefile.in0000664000175000017500000003562614772274255015242 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/etc/init ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(initdir)" DATA = $(init_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = keepalived.conf.in edit = echo " EDIT $@"; \ @SED@ \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' @INIT_UPSTART_TRUE@initdir = $(sysconfdir)/init @INIT_UPSTART_TRUE@init_DATA = keepalived.conf MOSTLYCLEANFILES = keepalived.conf all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/etc/init/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/etc/init/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-initDATA: $(init_DATA) @$(NORMAL_INSTALL) @list='$(init_DATA)'; test -n "$(initdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(initdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(initdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(initdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(initdir)" || exit $$?; \ done uninstall-initDATA: @$(NORMAL_UNINSTALL) @list='$(init_DATA)'; test -n "$(initdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(initdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(initdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-initDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-initDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic cscopelist-am \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-initDATA install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags-am uninstall uninstall-am \ uninstall-initDATA .PRECIOUS: Makefile keepalived.conf: $(builddir)/Makefile @rm -f $@ $@.tmp @$(edit) '$(srcdir)/$@.in' >$@ keepalived.conf: $(srcdir)/keepalived.conf.in # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/dbus/0000775000175000017500000000000014772274312012452 5keepalived-2.3.3/keepalived/dbus/org.keepalived.Vrrp1.Instance.xml0000664000175000017500000000157714413020216020561 keepalived-2.3.3/keepalived/dbus/Makefile.am0000664000175000017500000000330714122414147014420 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, # XML_MATCH is used for converting an .xml_template file into an # .xml file. Any lines in the .xml_template file that don't start with # a string of capital letters followed by a tab or new line will be passed # through to the .xml file. If XML_FILE comprises a capital letter, then lines # that have that capital letter in an initial string of capitals will also be # passed to the .xml file, but with the initial capitals and tab removed. # e.g. if XML_MATCH = C, then # ABCD fred # will be passed through as 'fred' and # ABXY joe # will not be passed # # Below are further more complex examples: # XML_MATCH = C[A-Z]*X # pass lines that have a C followed by an X (possibly with other letters in between) # XML_MATCH = [CX] # pass lines their have C or X (or both) # XML_MATCH = [CX][A-Z]*[CX] # pass lines with C and X in either order (or two C's or two X's) # # C[A-Z]*X is preferred if all option letters are in alphabetical order if DBUS_CREATE_INSTANCE XML_MATCH = C else XML_MATCH = None endif org.@PACKAGE@.Vrrp1.Vrrp.xml: Makefile .xml_template.xml: @echo " EDIT $@" @@SED@ -e "s/^[A-Z]*$(XML_MATCH)[A-Z]* //" -e "s/^[A-Z]*$(XML_MATCH)[A-Z]*$$//" -e "/^[A-Z]/d" $< >$@ EXTRA_DIST = org.@PACKAGE@.Vrrp1.Vrrp.xml_template dbusinterfacedir = @datadir@/dbus-1/interfaces dist_dbusinterface_DATA = org.@PACKAGE@.Vrrp1.Instance.xml inst_dbusinterfacedir = @datadir@/dbus-1/interfaces inst_dbusinterface_DATA = org.@PACKAGE@.Vrrp1.Vrrp.xml dbussystemdir = @sysconfdir@/dbus-1/system.d dist_dbussystem_DATA = org.@PACKAGE@.Vrrp1.conf mostlyclean-local: -rm -rf org.@PACKAGE@.Vrrp1.Vrrp.xml keepalived-2.3.3/keepalived/dbus/org.keepalived.Vrrp1.Vrrp.xml_template0000664000175000017500000000517414004226302021636 C C C C C C C C C C C keepalived-2.3.3/keepalived/dbus/org.keepalived.Vrrp1.conf0000664000175000017500000000122514147100503017133 keepalived-2.3.3/keepalived/dbus/Makefile.in0000664000175000017500000004502314772274255014451 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, # XML_MATCH is used for converting an .xml_template file into an # .xml file. Any lines in the .xml_template file that don't start with # a string of capital letters followed by a tab or new line will be passed # through to the .xml file. If XML_FILE comprises a capital letter, then lines # that have that capital letter in an initial string of capitals will also be # passed to the .xml file, but with the initial capitals and tab removed. # e.g. if XML_MATCH = C, then # ABCD fred # will be passed through as 'fred' and # ABXY joe # will not be passed # # Below are further more complex examples: # XML_MATCH = C[A-Z]*X # pass lines that have a C followed by an X (possibly with other letters in between) # XML_MATCH = [CX] # pass lines their have C or X (or both) # XML_MATCH = [CX][A-Z]*[CX] # pass lines with C and X in either order (or two C's or two X's) # # C[A-Z]*X is preferred if all option letters are in alphabetical order VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = keepalived/dbus ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(dist_dbusinterface_DATA) \ $(dist_dbussystem_DATA) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dbusinterfacedir)" \ "$(DESTDIR)$(dbussystemdir)" \ "$(DESTDIR)$(inst_dbusinterfacedir)" DATA = $(dist_dbusinterface_DATA) $(dist_dbussystem_DATA) \ $(inst_dbusinterface_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ @DBUS_CREATE_INSTANCE_FALSE@XML_MATCH = None @DBUS_CREATE_INSTANCE_TRUE@XML_MATCH = C EXTRA_DIST = org.@PACKAGE@.Vrrp1.Vrrp.xml_template dbusinterfacedir = @datadir@/dbus-1/interfaces dist_dbusinterface_DATA = org.@PACKAGE@.Vrrp1.Instance.xml inst_dbusinterfacedir = @datadir@/dbus-1/interfaces inst_dbusinterface_DATA = org.@PACKAGE@.Vrrp1.Vrrp.xml dbussystemdir = @sysconfdir@/dbus-1/system.d dist_dbussystem_DATA = org.@PACKAGE@.Vrrp1.conf all: all-am .SUFFIXES: .SUFFIXES: .xml .xml_template $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/dbus/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/dbus/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dist_dbusinterfaceDATA: $(dist_dbusinterface_DATA) @$(NORMAL_INSTALL) @list='$(dist_dbusinterface_DATA)'; test -n "$(dbusinterfacedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(dbusinterfacedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dbusinterfacedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbusinterfacedir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(dbusinterfacedir)" || exit $$?; \ done uninstall-dist_dbusinterfaceDATA: @$(NORMAL_UNINSTALL) @list='$(dist_dbusinterface_DATA)'; test -n "$(dbusinterfacedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(dbusinterfacedir)'; $(am__uninstall_files_from_dir) install-dist_dbussystemDATA: $(dist_dbussystem_DATA) @$(NORMAL_INSTALL) @list='$(dist_dbussystem_DATA)'; test -n "$(dbussystemdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(dbussystemdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dbussystemdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(dbussystemdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(dbussystemdir)" || exit $$?; \ done uninstall-dist_dbussystemDATA: @$(NORMAL_UNINSTALL) @list='$(dist_dbussystem_DATA)'; test -n "$(dbussystemdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(dbussystemdir)'; $(am__uninstall_files_from_dir) install-inst_dbusinterfaceDATA: $(inst_dbusinterface_DATA) @$(NORMAL_INSTALL) @list='$(inst_dbusinterface_DATA)'; test -n "$(inst_dbusinterfacedir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(inst_dbusinterfacedir)'"; \ $(MKDIR_P) "$(DESTDIR)$(inst_dbusinterfacedir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(inst_dbusinterfacedir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(inst_dbusinterfacedir)" || exit $$?; \ done uninstall-inst_dbusinterfaceDATA: @$(NORMAL_UNINSTALL) @list='$(inst_dbusinterface_DATA)'; test -n "$(inst_dbusinterfacedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(inst_dbusinterfacedir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(dbusinterfacedir)" "$(DESTDIR)$(dbussystemdir)" "$(DESTDIR)$(inst_dbusinterfacedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dist_dbusinterfaceDATA \ install-dist_dbussystemDATA install-inst_dbusinterfaceDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-local pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dist_dbusinterfaceDATA \ uninstall-dist_dbussystemDATA uninstall-inst_dbusinterfaceDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic cscopelist-am \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dist_dbusinterfaceDATA \ install-dist_dbussystemDATA install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-inst_dbusinterfaceDATA \ install-man install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-local pdf pdf-am \ ps ps-am tags-am uninstall uninstall-am \ uninstall-dist_dbusinterfaceDATA uninstall-dist_dbussystemDATA \ uninstall-inst_dbusinterfaceDATA .PRECIOUS: Makefile org.@PACKAGE@.Vrrp1.Vrrp.xml: Makefile .xml_template.xml: @echo " EDIT $@" @@SED@ -e "s/^[A-Z]*$(XML_MATCH)[A-Z]* //" -e "s/^[A-Z]*$(XML_MATCH)[A-Z]*$$//" -e "/^[A-Z]/d" $< >$@ mostlyclean-local: -rm -rf org.@PACKAGE@.Vrrp1.Vrrp.xml # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/Makefile.am0000664000175000017500000000704014555527250013473 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib AM_CPPFLAGS += $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) edit = echo " EDIT $@"; \ @SED@ \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@RUN_DIR[@]|$(RUN_DIR)|g' \ -e 's|@SNMP_SERVICE[@]|$(SNMP_SERVICE)|g' \ -e 's|@SYSTEMD_SERVICE_TYPE[@]|$(SYSTEMD_SERVICE_TYPE)|g' \ -e 's|@SYSTEMD_EXEC_START_OPTIONS[@]|$(SYSTEMD_EXEC_START_OPTIONS)|g' \ -e '/^Wants= *$$/d' \ -e 's|@CONFIG_OPTIONS[@]|$(KEEPALIVED_CONFIG_OPTIONS)|g' sbin_PROGRAMS = keepalived keepalived_SOURCES = main.c noinst_HEADERS = $(srcdir)/include/*.h TRACKER_SUBDIR = trackers TRACKER_LIB = trackers/libtracker.a if WITH_IPVS IPVS_SUBDIR = check IPVS_LIB = check/libcheck.a endif if WITH_VRRP VRRP_SUBDIR = vrrp VRRP_LIB = vrrp/libvrrp.a endif if WITH_DBUS DBUS_SUBDIR = dbus endif if WITH_BFD BFD_SUBDIR = bfd BFD_LIB = bfd/libbfd.a endif if INIT_SYSTEMD keepalived.service keepalived-non-root.service: $(builddir)/Makefile @rm -f $@ $@.tmp @$(edit) '$(srcdir)/$@.in' >$@ keepalived.service: $(srcdir)/keepalived.service.in keepalived-non-root.service: $(srcdir)/keepalived-non-root.service.in endif SUBDIRS = core $(VRRP_SUBDIR) $(IPVS_SUBDIR) $(DBUS_SUBDIR) $(BFD_SUBDIR) $(TRACKER_SUBDIR) etc EXTRA_DIST = keepalived.service.in keepalived-non-root.service.in keepalived.config-opts.in keepalived_LDADD = core/libcore.a $(IPVS_LIB) $(VRRP_LIB) $(BFD_LIB) core/libcore.a $(TRACKER_LIB) ../lib/liblib.a $(KA_LIBS) MOSTLYCLEANFILES = if INIT_SYSTEMD MOSTLYCLEANFILES += keepalived.service keeplived-non-root.service endif MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ if INIT_SYSTEMD systemdsystemunit_DATA = keepalived.service noinst_DATA = keepalived-non-root.service endif if INIT_OPENRC initdir = $(sysconfdir)/init.d init_DATA = etc/openrc/keepalived endif if REPRODUCIBLE_BUILD MOSTLYCLEANFILES += keepalived.config-opts configdir = @DEFAULT_CONFIG_DIR@ config_DATA = keepalived.config-opts keepalived.config-opts: $(builddir)/Makefile @rm -f $@ $@.tmp @$(edit) '$(srcdir)/$@.in' >$@ keepalived.config-opts: $(srcdir)/keepalived.config-opts.in endif if WITH_IPVS # checks for realpath, and also not busybox version which does not support --relative-to install-exec-hook: $(MKDIR_P) $(DESTDIR)/$(bindir) @( \ rm -f $(DESTDIR)/$(bindir)/genhash; \ realpath --relative-to=/ / >/dev/null 2>&1; \ if [ $$? -eq 0 ]; then \ $(LN_S) `realpath --relative-to="$(DESTDIR)/$(bindir)" "$(DESTDIR)/$(sbindir)/keepalived"` $(DESTDIR)/$(bindir)/genhash; \ else \ if [ $(bindir) = $(sbindir) ]; then \ d= ; \ s= ; \ else \ d=`echo $(bindir) | @SED@ -e "s:^/::"`; \ s=`echo $(sbindir) | @SED@ -e "s:^/::"`; \ \ while [ 1 ]; do \ d1=`echo $$d | @SED@ -e "s:/.*::"`; \ s1=`echo $$s | @SED@ -e "s:/.*::"`; \ if [ $$d1 != $$s1 ]; then \ break; \ fi; \ d=`echo $$d | @SED@ -e "s:^[^/]*/::"`; \ s=`echo $$s | @SED@ -e "s:^[^/]*/::"`; \ if [ -z $$d ]; then break; fi; \ if [ -z $$s ]; then break; fi; \ done; \ \ d=`echo /$$d/ | @SED@ -e "s:/[^/.]*/:/../:g" -e "s:/[^/.]*/:/../:g" -e "s:^/::"`; \ s=$$s/ ; \ fi; \ \ $(LN_S) $$d$${s}keepalived $(DESTDIR)/$(bindir)/genhash; \ fi; \ ) endif keepalived-2.3.3/keepalived/keepalived.service.in0000664000175000017500000000111614106247616015532 [Unit] Description=LVS and VRRP High Availability Monitor After=network-online.target syslog.target @SNMP_SERVICE@ Wants=network-online.target @SNMP_SERVICE@ Documentation=man:keepalived(8) Documentation=man:keepalived.conf(5) Documentation=man:genhash(1) Documentation=https://keepalived.org [Service] Type=@SYSTEMD_SERVICE_TYPE@ PIDFile=@RUN_DIR@/run/keepalived.pid KillMode=process EnvironmentFile=-@sysconfdir@/sysconfig/keepalived ExecStart=@sbindir@/keepalived @SYSTEMD_EXEC_START_OPTIONS@ $KEEPALIVED_OPTIONS ExecReload=/bin/kill -HUP $MAINPID [Install] WantedBy=multi-user.target keepalived-2.3.3/keepalived/check/0000775000175000017500000000000014772274312012572 5keepalived-2.3.3/keepalived/check/check_snmp.c0000664000175000017500000020117514613547140014772 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: SNMP agent * * Author: Vincent Bernat * * 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, */ #include "config.h" #include #include "check_snmp.h" #include "ipwrapper.h" #include "global_data.h" #include "snmp.h" #include "utils.h" #include "parser.h" /* CHECK SNMP defines */ #define CHECK_OID KEEPALIVED_OID, 3 enum check_snmp_vsgroup_magic { CHECK_SNMP_VSGROUPNAME = 2 }; enum check_snmp_vsgroupmember_magic { CHECK_SNMP_VSGROUPMEMBERTYPE = 2, CHECK_SNMP_VSGROUPMEMBERFWMARK, CHECK_SNMP_VSGROUPMEMBERADDRTYPE, CHECK_SNMP_VSGROUPMEMBERADDRESS, CHECK_SNMP_VSGROUPMEMBERADDR1, CHECK_SNMP_VSGROUPMEMBERADDR2, CHECK_SNMP_VSGROUPMEMBERPORT }; enum check_snmp_virtualserver_magic { CHECK_SNMP_VSTYPE = 2, CHECK_SNMP_VSNAMEGROUP, CHECK_SNMP_VSFWMARK, CHECK_SNMP_VSADDRTYPE, CHECK_SNMP_VSADDRESS, CHECK_SNMP_VSPORT, CHECK_SNMP_VSPROTOCOL, CHECK_SNMP_VSLOADBALANCINGALGO, CHECK_SNMP_VSLOADBALANCINGKIND, CHECK_SNMP_VSSTATUS, CHECK_SNMP_VSVIRTUALHOST, CHECK_SNMP_VSPERSIST, CHECK_SNMP_VSPERSISTTIMEOUT, CHECK_SNMP_VSPERSISTGRANULARITY, CHECK_SNMP_VSPERSISTGRANULARITY6, CHECK_SNMP_VSDELAYLOOP, CHECK_SNMP_VSHASUSPEND, CHECK_SNMP_VSOPS, CHECK_SNMP_VSALPHA, CHECK_SNMP_VSOMEGA, CHECK_SNMP_VSQUORUM, CHECK_SNMP_VSQUORUMSTATUS, CHECK_SNMP_VSQUORUMUP, CHECK_SNMP_VSQUORUMDOWN, CHECK_SNMP_VSHYSTERESIS, CHECK_SNMP_VSREALTOTAL, CHECK_SNMP_VSREALUP, CHECK_SNMP_VSSTATSCONNS, CHECK_SNMP_VSSTATSINPKTS, CHECK_SNMP_VSSTATSOUTPKTS, CHECK_SNMP_VSSTATSINBYTES, CHECK_SNMP_VSSTATSOUTBYTES, CHECK_SNMP_VSRATECPS, CHECK_SNMP_VSRATEINPPS, CHECK_SNMP_VSRATEOUTPPS, CHECK_SNMP_VSRATEINBPS, CHECK_SNMP_VSRATEOUTBPS, #ifdef _WITH_LVS_64BIT_STATS_ CHECK_SNMP_VSSTATSCONNS64, CHECK_SNMP_VSSTATSINPKTS64, CHECK_SNMP_VSSTATSOUTPKTS64, /* See below for VSRATECPS64 64 bit counters for rates */ CHECK_SNMP_VSRATECPSLOW, CHECK_SNMP_VSRATECPSHIGH, CHECK_SNMP_VSRATEINPPSLOW, CHECK_SNMP_VSRATEINPPSHIGH, CHECK_SNMP_VSRATEOUTPPSLOW, CHECK_SNMP_VSRATEOUTPPSHIGH, CHECK_SNMP_VSRATEINBPSLOW, CHECK_SNMP_VSRATEINBPSHIGH, CHECK_SNMP_VSRATEOUTBPSLOW, CHECK_SNMP_VSRATEOUTBPSHIGH, #endif CHECK_SNMP_VSHASHED, CHECK_SNMP_VSSHFALLBACK, CHECK_SNMP_VSSHPORT, CHECK_SNMP_VSMHFALLBACK, CHECK_SNMP_VSMHPORT, CHECK_SNMP_VSSCHED3, CHECK_SNMP_VSACTIONWHENDOWN, CHECK_SNMP_VSRETRY, CHECK_SNMP_VSDELAYBEFORERETRY, CHECK_SNMP_VSWARMUP, CHECK_SNMP_VSWEIGHT, CHECK_SNMP_VSSMTPALERT, CHECK_SNMP_VSDELAYLOOPUSEC, CHECK_SNMP_VSDELAYBEFORERETRYUSEC, CHECK_SNMP_VSWARMUPUSEC, CHECK_SNMP_VSCONNTIMEOUTUSEC, CHECK_SNMP_VSTUNNELTYPE, #ifdef _HAVE_IPVS_TUN_TYPE_ CHECK_SNMP_VSTUNNELPORT, #ifdef _HAVE_IPVS_TUN_CSUM_ CHECK_SNMP_VSTUNNELCSUM, #endif #endif CHECK_SNMP_VSNAME, CHECK_SNMP_VSQUORUMUPPATH, CHECK_SNMP_VSQUORUMDOWNPATH, #ifdef _WITH_LVS_64BIT_STATS_ CHECK_SNMP_VSRATECPS64, CHECK_SNMP_VSRATEINPPS64, CHECK_SNMP_VSRATEOUTPPS64, CHECK_SNMP_VSRATEINBPS64, CHECK_SNMP_VSRATEOUTBPS64, #endif }; enum check_snmp_realserver_magic { CHECK_SNMP_RSTYPE, CHECK_SNMP_RSADDRTYPE, CHECK_SNMP_RSADDRESS, CHECK_SNMP_RSPORT, CHECK_SNMP_RSSTATUS, CHECK_SNMP_RSWEIGHT, CHECK_SNMP_RSUPPERCONNECTIONLIMIT, CHECK_SNMP_RSLOWERCONNECTIONLIMIT, CHECK_SNMP_RSACTIONWHENDOWN, CHECK_SNMP_RSNOTIFYUP, CHECK_SNMP_RSNOTIFYDOWN, CHECK_SNMP_RSFAILEDCHECKS, CHECK_SNMP_RSSTATSCONNS, CHECK_SNMP_RSSTATSACTIVECONNS, CHECK_SNMP_RSSTATSINACTIVECONNS, CHECK_SNMP_RSSTATSPERSISTENTCONNS, CHECK_SNMP_RSSTATSINPKTS, CHECK_SNMP_RSSTATSOUTPKTS, CHECK_SNMP_RSSTATSINBYTES, CHECK_SNMP_RSSTATSOUTBYTES, CHECK_SNMP_RSRATECPS, CHECK_SNMP_RSRATEINPPS, CHECK_SNMP_RSRATEOUTPPS, CHECK_SNMP_RSRATEINBPS, CHECK_SNMP_RSRATEOUTBPS, #ifdef _WITH_LVS_64BIT_STATS_ CHECK_SNMP_RSSTATSCONNS64, CHECK_SNMP_RSSTATSINPKTS64, CHECK_SNMP_RSSTATSOUTPKTS64, /* See below for RSRATECPS64 etc 64 bit counters for rates */ CHECK_SNMP_RSRATECPSLOW, CHECK_SNMP_RSRATECPSHIGH, CHECK_SNMP_RSRATEINPPSLOW, CHECK_SNMP_RSRATEINPPSHIGH, CHECK_SNMP_RSRATEOUTPPSLOW, CHECK_SNMP_RSRATEOUTPPSHIGH, CHECK_SNMP_RSRATEINBPSLOW, CHECK_SNMP_RSRATEINBPSHIGH, CHECK_SNMP_RSRATEOUTBPSLOW, CHECK_SNMP_RSRATEOUTBPSHIGH, #endif CHECK_SNMP_RSLOADBALANCINGKIND, CHECK_SNMP_RSVIRTUALHOST, CHECK_SNMP_RSALPHA, CHECK_SNMP_RSRETRY, CHECK_SNMP_RSDELAYBEFORERETRY, CHECK_SNMP_RSWARMUP, CHECK_SNMP_RSDELAYLOOP, CHECK_SNMP_RSSMTPALERT, CHECK_SNMP_RSDELAYBEFORERETRYUSEC, CHECK_SNMP_RSWARMUPUSEC, CHECK_SNMP_RSDELAYLOOPUSEC, CHECK_SNMP_RSCONNTIMEOUTUSEC, CHECK_SNMP_RSTUNNELTYPE, #ifdef _HAVE_IPVS_TUN_TYPE_ CHECK_SNMP_RSTUNNELPORT, #ifdef _HAVE_IPVS_TUN_CSUM_ CHECK_SNMP_RSTUNNELCSUM, #endif #endif CHECK_SNMP_RSNAME, CHECK_SNMP_RSNOTIFYUPPATH, CHECK_SNMP_RSNOTIFYDOWNPATH, #ifdef _WITH_LVS_64BIT_STATS_ CHECK_SNMP_RSRATECPS64, CHECK_SNMP_RSRATEINPPS64, CHECK_SNMP_RSRATEOUTPPS64, CHECK_SNMP_RSRATEINBPS64, CHECK_SNMP_RSRATEOUTBPS64, #endif }; #define STATE_VSGM_FWMARK 1 #define STATE_VSGM_ADDRESS_RANGE 2 #define STATE_VSGM_END 3 #define STATE_RS_SORRY 1 #define STATE_RS_REGULAR_FIRST 2 #define STATE_RS_REGULAR_NEXT 3 #define STATE_RS_END 4 #ifdef _WITH_VRRP_ enum check_snmp_lvs_sync_daemon { CHECK_SNMP_LVSSYNCDAEMONENABLED, CHECK_SNMP_LVSSYNCDAEMONINTERFACE, CHECK_SNMP_LVSSYNCDAEMONVRRPINSTANCE, CHECK_SNMP_LVSSYNCDAEMONSYNCID, #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ CHECK_SNMP_LVSSYNCDAEMONMAXLEN, CHECK_SNMP_LVSSYNCDAEMONPORT, CHECK_SNMP_LVSSYNCDAEMONTTL, CHECK_SNMP_LVSSYNCDAEMONMCASTGROUPADDRTYPE, CHECK_SNMP_LVSSYNCDAEMONMCASTGROUPADDRVALUE, #endif }; #endif enum check_snmp_lvs_timeouts { CHECK_SNMP_LVSTIMEOUTTCP, CHECK_SNMP_LVSTIMEOUTTCPFIN, CHECK_SNMP_LVSTIMEOUTUDP, }; /* Macro */ #define RETURN_IP46ADDRESS(entity) \ do { \ if (entity->addr.ss_family == AF_INET6) { \ struct sockaddr_in6 *addr6 = PTR_CAST(struct sockaddr_in6, &entity->addr); \ *var_len = sizeof(struct in6_addr); \ return PTR_CAST(u_char, &addr6->sin6_addr); \ } else { \ struct sockaddr_in *addr4 = PTR_CAST(struct sockaddr_in, &entity->addr); \ *var_len = sizeof(struct in_addr); \ return PTR_CAST(u_char, &addr4->sin_addr); \ } \ } while(0) /* Static return values */ static longret_t long_ret; static char buf[MAXBUF]; static struct counter64 counter64_ret; static u_char* check_snmp_vsgroup(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { virtual_server_group_t *g; list_head_t *e; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &check_data->vs_group)) == NULL) return NULL; g = list_entry(e, virtual_server_group_t, e_list); switch (vp->magic) { case CHECK_SNMP_VSGROUPNAME: *var_len = strlen(g->gname); return PTR_CAST(u_char, g->gname); default: break; } return NULL; } static u_char* check_snmp_vsgroupmember(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { oid *target; oid current[2] = { 0 }; int result; size_t target_len; virtual_server_group_t *group; virtual_server_group_entry_t *vsge; int state; list_head_t *l; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } *write_method = 0; *var_len = sizeof(long); if (list_empty(&check_data->vs_group)) return NULL; /* We search the best match: equal if exact, the lower OID in the set of the OID strictly superior to the target otherwise. */ target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; list_for_each_entry(group, &check_data->vs_group, e_list) { current[0]++; current[1] = 0; if (target_len && (current[0] < target[0])) continue; /* Optimization: cannot be part of our set */ state = list_empty(&group->vfwmark) ? STATE_VSGM_ADDRESS_RANGE : STATE_VSGM_FWMARK; while (state < STATE_VSGM_END) { switch (state) { case STATE_VSGM_FWMARK: l = &group->vfwmark; break; case STATE_VSGM_ADDRESS_RANGE: l = &group->addr_range; break; default: /* Dunno? */ return NULL; } state++; list_for_each_entry(vsge, l, e_list) { current[1]++; /* And compare it to our target match */ if ((result = snmp_oid_compare(current, 2, target, target_len)) < 0) continue; if (result == 0) { if (!exact) continue; /* Got an exact match and asked for it */ } else { /* This is our best match */ target[0] = current[0]; target[1] = current[1]; *length = (unsigned)vp->namelen + 2; } goto vsgmember_found; } } } /* Nothing found */ return NULL; vsgmember_found: switch (vp->magic) { case CHECK_SNMP_VSGROUPMEMBERTYPE: if (vsge->is_fwmark) long_ret.u = 1; else if (inet_sockaddrcmp(&vsge->addr, &vsge->addr_end)) long_ret.u = 3; else long_ret.u = 2; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSGROUPMEMBERFWMARK: if (!vsge->is_fwmark) break; long_ret.u = vsge->vfwmark; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSGROUPMEMBERADDRTYPE: if (vsge->is_fwmark) break; long_ret.u = SNMP_InetAddressType(vsge->addr.ss_family); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSGROUPMEMBERADDRESS: if (vsge->is_fwmark || inet_sockaddrcmp(&vsge->addr, &vsge->addr_end)) break; RETURN_IP46ADDRESS(vsge); break; case CHECK_SNMP_VSGROUPMEMBERADDR1: if (vsge->is_fwmark || !inet_sockaddrcmp(&vsge->addr, &vsge->addr_end)) break; RETURN_IP46ADDRESS(vsge); break; case CHECK_SNMP_VSGROUPMEMBERADDR2: if (!inet_sockaddrcmp(&vsge->addr, &vsge->addr_end) || vsge->is_fwmark) break; if (vsge->addr.ss_family == AF_INET6) { struct sockaddr_in6 *addr6 = PTR_CAST(struct sockaddr_in6, &vsge->addr_end); *var_len = sizeof(addr6->sin6_addr); return PTR_CAST(u_char, &addr6->sin6_addr); } else { struct sockaddr_in *addr4 = PTR_CAST(struct sockaddr_in, &vsge->addr_end); *var_len = sizeof(addr4->sin_addr); return PTR_CAST(u_char, &addr4->sin_addr); } break; case CHECK_SNMP_VSGROUPMEMBERPORT: if (vsge->is_fwmark) break; long_ret.u = htons(inet_sockaddrport(&vsge->addr)); return PTR_CAST(u_char, &long_ret); default: return NULL; } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return check_snmp_vsgroupmember(vp, name, length, exact, var_len, write_method); return NULL; } static u_char * check_snmp_virtualserver(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { virtual_server_t *vs; real_server_t *rs; snmp_ret_t ret; list_head_t *e; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &check_data->vs)) == NULL) return NULL; vs = list_entry(e, virtual_server_t, e_list); switch (vp->magic) { case CHECK_SNMP_VSTYPE: if (vs->vsg) long_ret.u = 3; else if (vs->vfwmark) long_ret.u = 1; else long_ret.u = 2; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSNAMEGROUP: if (!vs->vsg) break; ret.cp = vs->vsgname; *var_len = strlen(ret.cp); return ret.p; case CHECK_SNMP_VSFWMARK: if (!vs->vfwmark) break; long_ret.u = vs->vfwmark; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSADDRTYPE: long_ret.u = SNMP_InetAddressType(vs->af); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSADDRESS: if (vs->vfwmark || vs->vsg) break; RETURN_IP46ADDRESS(vs); break; case CHECK_SNMP_VSPORT: if (vs->vfwmark || vs->vsg) break; long_ret.u = htons(inet_sockaddrport(&vs->addr)); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSPROTOCOL: if (vs->vfwmark) break; long_ret.u = (vs->service_type == IPPROTO_TCP) ? 1 : (vs->service_type == IPPROTO_UDP) ? 2 : (vs->service_type == IPPROTO_SCTP) ? 3 : 4; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSLOADBALANCINGALGO: if (!strcmp(vs->sched, "rr")) long_ret.u = 1; else if (!strcmp(vs->sched, "wrr")) long_ret.u = 2; else if (!strcmp(vs->sched, "lc")) long_ret.u = 3; else if (!strcmp(vs->sched, "wlc")) long_ret.u = 4; else if (!strcmp(vs->sched, "lblc")) long_ret.u = 5; else if (!strcmp(vs->sched, "lblcr")) long_ret.u = 6; else if (!strcmp(vs->sched, "dh")) long_ret.u = 7; else if (!strcmp(vs->sched, "sh")) long_ret.u = 8; else if (!strcmp(vs->sched, "sed")) long_ret.u = 9; else if (!strcmp(vs->sched, "nq")) long_ret.u = 10; else if (!strcmp(vs->sched, "fo")) long_ret.u = 11; else if (!strcmp(vs->sched, "ovf")) long_ret.u = 12; else if (!strcmp(vs->sched, "mh")) long_ret.u = 13; else if (!strcmp(vs->sched, "twos")) long_ret.u = 14; else long_ret.u = 99; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSLOADBALANCINGKIND: long_ret.u = 0; switch (vs->forwarding_method) { case IP_VS_CONN_F_MASQ: long_ret.u = 1; break; case IP_VS_CONN_F_DROUTE: long_ret.u = 2; break; case IP_VS_CONN_F_TUNNEL: long_ret.u = 3; break; } if (!long_ret.u) break; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSTATUS: long_ret.u = SNMP_TruthValue(vs->alive); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSVIRTUALHOST: if (!vs->virtualhost) break; *var_len = strlen(vs->virtualhost); ret.cp = vs->virtualhost; return ret.p; case CHECK_SNMP_VSPERSIST: long_ret.u = SNMP_TruthValue(vs->persistence_timeout); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSPERSISTTIMEOUT: if (!vs->persistence_timeout) break; long_ret.u = vs->persistence_timeout; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSPERSISTGRANULARITY: if (vs->addr.ss_family == AF_INET6) break; *var_len = sizeof(vs->persistence_granularity); return PTR_CAST(u_char, &vs->persistence_granularity); case CHECK_SNMP_VSPERSISTGRANULARITY6: if (vs->addr.ss_family == AF_INET) break; *var_len = sizeof(vs->persistence_granularity); return PTR_CAST(u_char, &vs->persistence_granularity); case CHECK_SNMP_VSDELAYLOOP: long_ret.u = vs->delay_loop/TIMER_HZ; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSHASUSPEND: long_ret.u = SNMP_TruthValue(vs->ha_suspend); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSOPS: long_ret.u = SNMP_TruthValue(vs->flags & IP_VS_SVC_F_ONEPACKET); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSALPHA: long_ret.u = SNMP_TruthValue(vs->alpha); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSOMEGA: long_ret.u = SNMP_TruthValue(vs->omega); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSQUORUM: long_ret.u = vs->quorum; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSQUORUMSTATUS: long_ret.u = SNMP_TruthValue(vs->quorum_state_up); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSQUORUMUP: if (!vs->notify_quorum_up) break; cmd_str_r(vs->notify_quorum_up, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); case CHECK_SNMP_VSQUORUMDOWN: if (!vs->notify_quorum_down) break; cmd_str_r(vs->notify_quorum_down, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); case CHECK_SNMP_VSHYSTERESIS: long_ret.u = vs->hysteresis; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSREALTOTAL: long_ret.u = vs->rs_cnt; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSREALUP: long_ret.u = 0; list_for_each_entry(rs, &vs->rs, e_list) if (rs->alive) long_ret.u++; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSTATSCONNS: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.conns; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSTATSINPKTS: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.inpkts; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSTATSOUTPKTS: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.outpkts; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSTATSINBYTES: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.inbytes & 0xffffffff; counter64_ret.high = vs->stats.inbytes >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_VSSTATSOUTBYTES: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.outbytes & 0xffffffff; counter64_ret.high = vs->stats.outbytes >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_VSRATECPS: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.cps; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEINPPS: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.inpps; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEOUTPPS: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.outpps; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEINBPS: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.inbps; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEOUTBPS: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.outbps; return PTR_CAST(u_char, &long_ret); #ifdef _WITH_LVS_64BIT_STATS_ case CHECK_SNMP_VSSTATSCONNS64: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.conns & 0xffffffff; counter64_ret.high = vs->stats.conns >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_VSSTATSINPKTS64: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.inpkts & 0xffffffff; counter64_ret.high = vs->stats.inpkts >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_VSSTATSOUTPKTS64: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.outpkts & 0xffffffff; counter64_ret.high = vs->stats.outpkts >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); /* See below for VSRATECPS64 etc 64 bit counters for rates */ case CHECK_SNMP_VSRATECPSLOW: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.cps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATECPSHIGH: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.cps >> 32; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEINPPSLOW: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.inpps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEINPPSHIGH: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.inpps >> 32; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEOUTPPSLOW: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.outpps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEOUTPPSHIGH: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.outpps >> 32; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEINBPSLOW: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.inbps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEINBPSHIGH: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.inbps >> 32; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEOUTBPSLOW: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.outbps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRATEOUTBPSHIGH: ipvs_vs_update_stats(vs); long_ret.u = vs->stats.outbps >> 32; return PTR_CAST(u_char, &long_ret); #endif #ifdef IP_VS_SVC_F_SCHED1 case CHECK_SNMP_VSHASHED: long_ret.u = SNMP_TruthValue(vs->flags & IP_VS_SVC_F_HASHED); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSHFALLBACK: long_ret.u = SNMP_TruthValue(vs->flags & IP_VS_SVC_F_SCHED_SH_FALLBACK); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSHPORT: long_ret.u = SNMP_TruthValue(vs->flags & IP_VS_SVC_F_SCHED_SH_PORT); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSMHFALLBACK: long_ret.u = SNMP_TruthValue(vs->flags & IP_VS_SVC_F_SCHED_MH_FALLBACK); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSMHPORT: long_ret.u = SNMP_TruthValue(vs->flags & IP_VS_SVC_F_SCHED_MH_PORT); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSCHED3: long_ret.u = SNMP_TruthValue(vs->flags & IP_VS_SVC_F_SCHED3); return PTR_CAST(u_char, &long_ret); #endif case CHECK_SNMP_VSACTIONWHENDOWN: long_ret.u = SNMP_TruthValue(!vs->inhibit); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSRETRY: long_ret.u = vs->retry == UINT_MAX ? 0 : vs->retry; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSDELAYBEFORERETRY: long_ret.u = vs->delay_before_retry == ULONG_MAX ? 0 : vs->delay_before_retry / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSWARMUP: long_ret.u = vs->warmup == ULONG_MAX ? 0 : vs->warmup / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSWEIGHT: long_ret.s = vs->weight; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSSMTPALERT: long_ret.u = SNMP_TruthValue(vs->smtp_alert); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSDELAYLOOPUSEC: long_ret.u = vs->delay_loop; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSDELAYBEFORERETRYUSEC: long_ret.u = vs->delay_before_retry == ULONG_MAX ? 0 : vs->delay_before_retry; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSWARMUPUSEC: long_ret.u = vs->warmup == ULONG_MAX ? 0 : vs->warmup; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSCONNTIMEOUTUSEC: long_ret.u = vs->connection_to; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_VSTUNNELTYPE: if (vs->forwarding_method != IP_VS_CONN_F_TUNNEL) break; #ifndef _HAVE_IPVS_TUN_TYPE_ long_ret.u = 1; /* IPIP */ #else long_ret.u = vs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_IPIP ? 1 : vs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE ? 2 #ifdef _HAVE_IPVS_TUN_GRE_ : vs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE ? 3 #endif : 0; #endif return PTR_CAST(u_char, &long_ret); #ifdef _HAVE_IPVS_TUN_TYPE_ case CHECK_SNMP_VSTUNNELPORT: if (vs->forwarding_method != IP_VS_CONN_F_TUNNEL || vs->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_GUE) break; long_ret.u = ntohs(vs->tun_port); return PTR_CAST(u_char, &long_ret); #ifdef _HAVE_IPVS_TUN_CSUM_ case CHECK_SNMP_VSTUNNELCSUM: if (vs->forwarding_method != IP_VS_CONN_F_TUNNEL || vs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_IPIP) break; long_ret.u = vs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM ? 1 : vs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_CSUM ? 2 : vs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM ? 3 : 0; return PTR_CAST(u_char, &long_ret); #endif #endif case CHECK_SNMP_VSNAME: if (!vs->snmp_name) break; *var_len = strlen(vs->snmp_name); ret.cp = vs->snmp_name; return ret.p; case CHECK_SNMP_VSQUORUMUPPATH: if (!vs->notify_quorum_up) break; ret.cp = vs->notify_quorum_up->path ? vs->notify_quorum_up->path : vs->notify_quorum_up->args[0] ; *var_len = strlen(ret.cp); return ret.p; case CHECK_SNMP_VSQUORUMDOWNPATH: if (!vs->notify_quorum_down) break; ret.cp = vs->notify_quorum_down->path ? vs->notify_quorum_down->path : vs->notify_quorum_down->args[0]; *var_len = strlen(ret.cp); return ret.p; #ifdef _WITH_LVS_64BIT_STATS_ case CHECK_SNMP_VSRATECPS64: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.cps & 0xffffffff; counter64_ret.high = vs->stats.cps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_VSRATEINPPS64: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.inpps & 0xffffffff; counter64_ret.high = vs->stats.inpps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_VSRATEOUTPPS64: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.outpps & 0xffffffff; counter64_ret.high = vs->stats.outpps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_VSRATEINBPS64: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.inbps & 0xffffffff; counter64_ret.high = vs->stats.inbps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_VSRATEOUTBPS64: ipvs_vs_update_stats(vs); counter64_ret.low = vs->stats.outbps & 0xffffffff; counter64_ret.high = vs->stats.outbps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); #endif default: return NULL; } if (!exact && (name[*length-1] < MAX_SUBID)) return check_snmp_virtualserver(vp, name, length, exact, var_len, write_method); return NULL; } static int check_snmp_realserver_weight(int action, u_char *var_val, u_char var_val_type, size_t var_val_len, __attribute__((unused)) u_char *statP, oid *name, size_t name_len) { virtual_server_t *vs = NULL; real_server_t *rs = NULL; oid ivs, irs; switch (action) { case RESERVE1: /* Check that the proposed value is acceptable */ if (var_val_type != ASN_INTEGER) return SNMP_ERR_WRONGTYPE; if (var_val_len > sizeof(long)) return SNMP_ERR_WRONGLENGTH; break; case RESERVE2: /* Check that we can find the instance. We should. */ case COMMIT: /* Find the instance */ if (name_len < 2) return SNMP_ERR_NOSUCHNAME; irs = name[name_len - 1]; ivs = name[name_len - 2]; if (list_empty(&check_data->vs)) return SNMP_ERR_NOSUCHNAME; list_for_each_entry(vs, &check_data->vs, e_list) { if (--ivs == 0) { if (vs->s_svr) { /* We don't want to set weight of sorry server */ rs = NULL; if (--irs == 0) break; } list_for_each_entry(rs, &vs->rs, e_list) { if (--irs == 0) break; } break; } } /* Did not find a RS or this is a sorry server (this should not happen) */ if (!rs) return SNMP_ERR_NOSUCHNAME; if (action == RESERVE2) break; /* Commit: change values. There is no way to fail. */ update_svr_wgt((unsigned)(*var_val), vs, rs, true); break; } return SNMP_ERR_NOERROR; } static u_char * check_snmp_realserver(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { oid *target; oid current[2] = { 0 }; int result; size_t target_len; real_server_t *rs; virtual_server_t *vs; int state; int type; snmp_ret_t ret; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } *write_method = 0; *var_len = sizeof(long); if (list_empty(&check_data->vs)) return NULL; /* We search the best match: equal if exact, the lower OID in the set of the OID strictly superior to the target otherwise. */ target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; list_for_each_entry(vs, &check_data->vs, e_list) { current[0]++; current[1] = 0; if (target_len && (current[0] < target[0])) continue; /* Optimization: cannot be part of our set */ state = vs->s_svr ? STATE_RS_SORRY : STATE_RS_REGULAR_FIRST; while (state != STATE_RS_END) { switch (state) { case STATE_RS_SORRY: rs = vs->s_svr; type = STATE_RS_SORRY; state = STATE_RS_REGULAR_FIRST; break; case STATE_RS_REGULAR_FIRST: if (list_empty(&vs->rs)) { rs = NULL; state = STATE_RS_END; break; } rs = list_first_entry(&vs->rs, real_server_t, e_list); type = STATE_RS_REGULAR_FIRST; state = STATE_RS_REGULAR_NEXT; break; case STATE_RS_REGULAR_NEXT: type = STATE_RS_REGULAR_NEXT; if (list_is_last(&rs->e_list, &vs->rs)) { rs = NULL; state = STATE_RS_END; break; } rs = list_entry(rs->e_list.next, real_server_t, e_list); break; default: /* Dunno? */ return NULL; } if (!rs) continue; current[1]++; /* And compare it to our target match */ if ((result = snmp_oid_compare(current, 2, target, target_len)) < 0) continue; if (result == 0) { /* Got an exact match. Were we asked for it? */ if (!exact) continue; } else { target[0] = current[0]; target[1] = current[1]; *length = (unsigned)vp->namelen + 2; } break; } if (rs) break; } if (rs == NULL) { /* No match */ return NULL; } switch (vp->magic) { case CHECK_SNMP_RSTYPE: long_ret.u = SNMP_TruthValue(type != STATE_RS_SORRY); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSADDRTYPE: long_ret.u = SNMP_InetAddressType(rs->addr.ss_family); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSADDRESS: RETURN_IP46ADDRESS(rs); break; case CHECK_SNMP_RSPORT: long_ret.u = htons(inet_sockaddrport(&rs->addr)); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSLOADBALANCINGKIND: switch (rs->forwarding_method) { case IP_VS_CONN_F_MASQ: long_ret.u = 1; break; case IP_VS_CONN_F_DROUTE: long_ret.u = 2; break; case IP_VS_CONN_F_TUNNEL: long_ret.u = 3; break; default: long_ret.u = 0; break; } if (!long_ret.u) break; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSTATUS: if (type == STATE_RS_SORRY) break; long_ret.u = SNMP_TruthValue(rs->alive); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSWEIGHT: if (type == STATE_RS_SORRY) break; long_ret.s = real_weight(rs->effective_weight); *write_method = check_snmp_realserver_weight; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSUPPERCONNECTIONLIMIT: if (type == STATE_RS_SORRY) break; if (!rs->u_threshold) break; long_ret.u = rs->u_threshold; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSLOWERCONNECTIONLIMIT: if (type == STATE_RS_SORRY) break; if (!rs->l_threshold) break; long_ret.u = rs->l_threshold; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSACTIONWHENDOWN: if (type == STATE_RS_SORRY) break; long_ret.u = SNMP_TruthValue(!rs->inhibit); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSNOTIFYUP: if (type == STATE_RS_SORRY) break; if (!rs->notify_up) break; cmd_str_r(rs->notify_up, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); case CHECK_SNMP_RSNOTIFYDOWN: if (type == STATE_RS_SORRY) break; if (!rs->notify_down) break; cmd_str_r(rs->notify_down, buf, sizeof(buf)); *var_len = strlen(buf); return PTR_CAST(u_char, buf); case CHECK_SNMP_RSVIRTUALHOST: if (!rs->virtualhost) break; *var_len = strlen(rs->virtualhost); ret.cp = rs->virtualhost; return ret.p; case CHECK_SNMP_RSFAILEDCHECKS: if (type == STATE_RS_SORRY) break; long_ret.u = rs->num_failed_checkers; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSTATSCONNS: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.conns; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSTATSACTIVECONNS: ipvs_rs_update_stats(vs); long_ret.u = rs->activeconns; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSTATSINACTIVECONNS: ipvs_rs_update_stats(vs); long_ret.u = rs->inactconns; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSTATSPERSISTENTCONNS: ipvs_rs_update_stats(vs); long_ret.u = rs->persistconns; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSTATSINPKTS: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.inpkts; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSTATSOUTPKTS: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.outpkts; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSTATSINBYTES: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.inbytes & 0xffffffff; counter64_ret.high = rs->stats.inbytes >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_RSSTATSOUTBYTES: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.outbytes & 0xffffffff; counter64_ret.high = rs->stats.outbytes >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_RSRATECPS: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.cps; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEINPPS: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.inpps; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEOUTPPS: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.outpps; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEINBPS: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.inbps; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEOUTBPS: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.outbps; return PTR_CAST(u_char, &long_ret); #ifdef _WITH_LVS_64BIT_STATS_ case CHECK_SNMP_RSSTATSCONNS64: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.conns & 0xffffffff; counter64_ret.high = rs->stats.conns >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_RSSTATSINPKTS64: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.inpkts & 0xffffffff; counter64_ret.high = rs->stats.inpkts >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_RSSTATSOUTPKTS64: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.outpkts & 0xffffffff; counter64_ret.high = rs->stats.outpkts >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); /* See below for RSRATECPS64 etc 64 bit counters for rates */ case CHECK_SNMP_RSRATECPSLOW: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.cps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATECPSHIGH: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.cps >> 32; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEINPPSLOW: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.inpps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEINPPSHIGH: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.inpps >> 32; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEOUTPPSLOW: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.outpps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEOUTPPSHIGH: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.outpps >> 32; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEINBPSLOW: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.inbps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEINBPSHIGH: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.inbps >> 32; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEOUTBPSLOW: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.outbps & 0xffffffff; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRATEOUTBPSHIGH: ipvs_rs_update_stats(vs); long_ret.u = rs->stats.outbps >> 32; return PTR_CAST(u_char, &long_ret); #endif case CHECK_SNMP_RSALPHA: long_ret.u = SNMP_TruthValue(rs->alpha); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSRETRY: long_ret.u = rs->retry == UINT_MAX ? 0 : rs->retry; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSDELAYBEFORERETRY: long_ret.u = rs->delay_before_retry == ULONG_MAX ? 0 : rs->delay_before_retry / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSWARMUP: long_ret.u = rs->warmup == ULONG_MAX ? 0 : rs->warmup / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSDELAYLOOP: long_ret.u = rs->delay_loop == ULONG_MAX ? 0 : rs->delay_loop / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSSMTPALERT: long_ret.u = SNMP_TruthValue(rs->smtp_alert); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSDELAYBEFORERETRYUSEC: long_ret.u = rs->delay_before_retry == ULONG_MAX ? 0 : rs->delay_before_retry; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSWARMUPUSEC: long_ret.u = rs->warmup == ULONG_MAX ? 0 : rs->warmup; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSDELAYLOOPUSEC: long_ret.u = rs->delay_loop == ULONG_MAX ? 0 : rs->delay_loop; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSCONNTIMEOUTUSEC: long_ret.u = rs->connection_to; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_RSTUNNELTYPE: if (rs->forwarding_method != IP_VS_CONN_F_TUNNEL) break; #ifndef _HAVE_IPVS_TUN_TYPE_ long_ret.u = 1; /* IPIP */ #else long_ret.u = rs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_IPIP ? 1 : rs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE ? 2 #ifdef _HAVE_IPVS_TUN_GRE_ : rs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE ? 3 #endif : 0; #endif return PTR_CAST(u_char, &long_ret); #ifdef _HAVE_IPVS_TUN_TYPE_ case CHECK_SNMP_RSTUNNELPORT: if (rs->forwarding_method != IP_VS_CONN_F_TUNNEL || rs->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_GUE) break; long_ret.u = ntohs(rs->tun_port); return PTR_CAST(u_char, &long_ret); #ifdef _HAVE_IPVS_TUN_CSUM_ case CHECK_SNMP_RSTUNNELCSUM: if (rs->forwarding_method != IP_VS_CONN_F_TUNNEL || rs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_IPIP) break; long_ret.u = rs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM ? 1 : rs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_CSUM ? 2 : rs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM ? 3 : 0; return PTR_CAST(u_char, &long_ret); #endif #endif case CHECK_SNMP_RSNAME: if (!rs->snmp_name) break; *var_len = strlen(rs->snmp_name); ret.cp = rs->snmp_name; return ret.p; case CHECK_SNMP_RSNOTIFYUPPATH: if (type == STATE_RS_SORRY) break; if (!rs->notify_up) break; ret.cp = rs->notify_up->path ? rs->notify_up->path : rs->notify_up->args[0]; *var_len = strlen(ret.cp); return ret.p; case CHECK_SNMP_RSNOTIFYDOWNPATH: if (type == STATE_RS_SORRY) break; if (!rs->notify_down) break; ret.cp = rs->notify_down->path ? rs->notify_down->path : rs->notify_down->args[0]; *var_len = strlen(ret.cp); return ret.p; #ifdef _WITH_LVS_64BIT_STATS_ case CHECK_SNMP_RSRATECPS64: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.cps & 0xffffffff; counter64_ret.high = rs->stats.cps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_RSRATEINPPS64: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.inpps & 0xffffffff; counter64_ret.high = rs->stats.inpps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_RSRATEOUTPPS64: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.outpps & 0xffffffff; counter64_ret.high = rs->stats.outpps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_RSRATEINBPS64: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.inbps & 0xffffffff; counter64_ret.high = rs->stats.inbps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); case CHECK_SNMP_RSRATEOUTBPS64: ipvs_rs_update_stats(vs); counter64_ret.low = rs->stats.outbps & 0xffffffff; counter64_ret.high = rs->stats.outbps >> 32; *var_len = sizeof(struct counter64); return PTR_CAST(u_char, &counter64_ret); #endif default: return NULL; } /* If we are here, we asked for a non existent data. Try the next one. */ if (!exact && (name[*length-1] < MAX_SUBID)) return check_snmp_realserver(vp, name, length, exact, var_len, write_method); return NULL; } #ifdef _WITH_VRRP_ static u_char* check_snmp_lvs_sync_daemon(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { snmp_ret_t ret; if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; switch (vp->magic) { case CHECK_SNMP_LVSSYNCDAEMONENABLED: long_ret.u = SNMP_TruthValue(global_data->lvs_syncd.ifname); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_LVSSYNCDAEMONINTERFACE: if (!global_data->lvs_syncd.ifname) return NULL; *var_len = strlen(global_data->lvs_syncd.ifname); ret.cp = global_data->lvs_syncd.ifname; return ret.p; case CHECK_SNMP_LVSSYNCDAEMONVRRPINSTANCE: if (!global_data->lvs_syncd.ifname || !global_data->lvs_syncd.vrrp_name) return NULL; *var_len = strlen(global_data->lvs_syncd.vrrp_name); ret.cp = global_data->lvs_syncd.vrrp_name; return ret.p; case CHECK_SNMP_LVSSYNCDAEMONSYNCID: if (!global_data->lvs_syncd.ifname) return NULL; long_ret.u = global_data->lvs_syncd.syncid; return PTR_CAST(u_char, &long_ret); #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ case CHECK_SNMP_LVSSYNCDAEMONMAXLEN: if (!global_data->lvs_syncd.ifname) return NULL; long_ret.u = global_data->lvs_syncd.sync_maxlen; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_LVSSYNCDAEMONPORT: if (!global_data->lvs_syncd.ifname) return NULL; long_ret.u = global_data->lvs_syncd.mcast_port; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_LVSSYNCDAEMONTTL: if (!global_data->lvs_syncd.ifname) return NULL; long_ret.u = global_data->lvs_syncd.mcast_ttl; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_LVSSYNCDAEMONMCASTGROUPADDRTYPE: if (!global_data->lvs_syncd.ifname || global_data->lvs_syncd.mcast_group.ss_family == AF_UNSPEC) return NULL; long_ret.u = SNMP_InetAddressType(global_data->lvs_syncd.mcast_group.ss_family); return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_LVSSYNCDAEMONMCASTGROUPADDRVALUE: if (!global_data->lvs_syncd.ifname || global_data->lvs_syncd.mcast_group.ss_family == AF_UNSPEC) return NULL; if (global_data->lvs_syncd.mcast_group.ss_family == AF_INET6) { struct sockaddr_in6 *addr6 = PTR_CAST(struct sockaddr_in6, &global_data->lvs_syncd.mcast_group); *var_len = sizeof(addr6->sin6_addr); return PTR_CAST(u_char, &addr6->sin6_addr); } else { struct sockaddr_in *addr4 = PTR_CAST(struct sockaddr_in, &global_data->lvs_syncd.mcast_group); *var_len = sizeof(addr4->sin_addr); return PTR_CAST(u_char, &addr4->sin_addr); } #endif } return NULL; } #endif static u_char* check_snmp_lvs_timeouts(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; switch (vp->magic) { case CHECK_SNMP_LVSTIMEOUTTCP: if (!global_data->lvs_timeouts.tcp_timeout) return NULL; long_ret.s = global_data->lvs_timeouts.tcp_timeout; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_LVSTIMEOUTTCPFIN: if (!global_data->lvs_timeouts.tcp_fin_timeout) return NULL; long_ret.s = global_data->lvs_timeouts.tcp_fin_timeout; return PTR_CAST(u_char, &long_ret); case CHECK_SNMP_LVSTIMEOUTUDP: if (!global_data->lvs_timeouts.udp_timeout) return NULL; long_ret.s = global_data->lvs_timeouts.udp_timeout; return PTR_CAST(u_char, &long_ret); } return NULL; } static oid check_oid[] = {CHECK_OID}; static struct variable3 check_vars[] = { /* virtualServerGroupTable */ {CHECK_SNMP_VSGROUPNAME, ASN_OCTET_STR, RONLY, check_snmp_vsgroup, 3, {1, 1, 2}}, /* virtualServerGroupMemberTable */ {CHECK_SNMP_VSGROUPMEMBERTYPE, ASN_INTEGER, RONLY, check_snmp_vsgroupmember, 3, {2, 1, 2}}, {CHECK_SNMP_VSGROUPMEMBERFWMARK, ASN_UNSIGNED, RONLY, check_snmp_vsgroupmember, 3, {2, 1, 3}}, {CHECK_SNMP_VSGROUPMEMBERADDRTYPE, ASN_INTEGER, RONLY, check_snmp_vsgroupmember, 3, {2, 1, 4}}, {CHECK_SNMP_VSGROUPMEMBERADDRESS, ASN_OCTET_STR, RONLY, check_snmp_vsgroupmember, 3, {2, 1, 5}}, {CHECK_SNMP_VSGROUPMEMBERADDR1, ASN_OCTET_STR, RONLY, check_snmp_vsgroupmember, 3, {2, 1, 6}}, {CHECK_SNMP_VSGROUPMEMBERADDR2, ASN_OCTET_STR, RONLY, check_snmp_vsgroupmember, 3, {2, 1, 7}}, {CHECK_SNMP_VSGROUPMEMBERPORT, ASN_UNSIGNED, RONLY, check_snmp_vsgroupmember, 3, {2, 1, 8}}, /* virtualServerTable */ {CHECK_SNMP_VSTYPE, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 2}}, {CHECK_SNMP_VSNAMEGROUP, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 3}}, {CHECK_SNMP_VSFWMARK, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 4}}, {CHECK_SNMP_VSADDRTYPE, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 5}}, {CHECK_SNMP_VSADDRESS, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 6}}, {CHECK_SNMP_VSPORT, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 7}}, {CHECK_SNMP_VSPROTOCOL, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 8}}, {CHECK_SNMP_VSLOADBALANCINGALGO, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 9}}, {CHECK_SNMP_VSLOADBALANCINGKIND, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 10}}, {CHECK_SNMP_VSSTATUS, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 11}}, {CHECK_SNMP_VSVIRTUALHOST, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 12}}, {CHECK_SNMP_VSPERSIST, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 13}}, {CHECK_SNMP_VSPERSISTTIMEOUT, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 14}}, {CHECK_SNMP_VSPERSISTGRANULARITY, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 15}}, {CHECK_SNMP_VSDELAYLOOP, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 16}}, {CHECK_SNMP_VSHASUSPEND, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 17}}, {CHECK_SNMP_VSOPS, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 37}}, {CHECK_SNMP_VSALPHA, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 18}}, {CHECK_SNMP_VSOMEGA, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 19}}, {CHECK_SNMP_VSREALTOTAL, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 20}}, {CHECK_SNMP_VSREALUP, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 21}}, {CHECK_SNMP_VSQUORUM, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 22}}, {CHECK_SNMP_VSQUORUMSTATUS, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 23}}, {CHECK_SNMP_VSQUORUMUP, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 24}}, {CHECK_SNMP_VSQUORUMDOWN, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 25}}, {CHECK_SNMP_VSHYSTERESIS, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 26}}, {CHECK_SNMP_VSSTATSCONNS, ASN_GAUGE, RONLY, check_snmp_virtualserver, 3, {3, 1, 27}}, {CHECK_SNMP_VSSTATSINPKTS, ASN_COUNTER, RONLY, check_snmp_virtualserver, 3, {3, 1, 28}}, {CHECK_SNMP_VSSTATSOUTPKTS, ASN_COUNTER, RONLY, check_snmp_virtualserver, 3, {3, 1, 29}}, {CHECK_SNMP_VSSTATSINBYTES, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 30}}, {CHECK_SNMP_VSSTATSOUTBYTES, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 31}}, {CHECK_SNMP_VSRATECPS, ASN_GAUGE, RONLY, check_snmp_virtualserver, 3, {3, 1, 32}}, {CHECK_SNMP_VSRATEINPPS, ASN_GAUGE, RONLY, check_snmp_virtualserver, 3, {3, 1, 33}}, {CHECK_SNMP_VSRATEOUTPPS, ASN_GAUGE, RONLY, check_snmp_virtualserver, 3, {3, 1, 34}}, {CHECK_SNMP_VSRATEINBPS, ASN_GAUGE, RONLY, check_snmp_virtualserver, 3, {3, 1, 35}}, {CHECK_SNMP_VSRATEOUTBPS, ASN_GAUGE, RONLY, check_snmp_virtualserver, 3, {3, 1, 36}}, #ifdef _WITH_LVS_64BIT_STATS_ {CHECK_SNMP_VSSTATSCONNS64, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 38}}, {CHECK_SNMP_VSSTATSINPKTS64, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 39}}, {CHECK_SNMP_VSSTATSOUTPKTS64, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 40}}, /* See below for VSRATECPS64 etc 64 bit counters for rates */ {CHECK_SNMP_VSRATECPSLOW, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 41}}, {CHECK_SNMP_VSRATECPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 42}}, {CHECK_SNMP_VSRATEINPPSLOW, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 43}}, {CHECK_SNMP_VSRATEINPPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 44}}, {CHECK_SNMP_VSRATEOUTPPSLOW, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 45}}, {CHECK_SNMP_VSRATEOUTPPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 46}}, {CHECK_SNMP_VSRATEINBPSLOW, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 47}}, {CHECK_SNMP_VSRATEINBPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 48}}, {CHECK_SNMP_VSRATEOUTBPSLOW, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 49}}, {CHECK_SNMP_VSRATEOUTBPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 50}}, #endif {CHECK_SNMP_VSPERSISTGRANULARITY6, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 51}}, {CHECK_SNMP_VSHASHED, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 52}}, {CHECK_SNMP_VSSHFALLBACK, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 53}}, {CHECK_SNMP_VSSHPORT, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 54}}, {CHECK_SNMP_VSSCHED3, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 55}}, {CHECK_SNMP_VSACTIONWHENDOWN, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 56}}, {CHECK_SNMP_VSRETRY, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 57}}, {CHECK_SNMP_VSDELAYBEFORERETRY, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 58}}, {CHECK_SNMP_VSWARMUP, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 59}}, {CHECK_SNMP_VSWEIGHT, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 60}}, {CHECK_SNMP_VSSMTPALERT, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 61}}, {CHECK_SNMP_VSMHFALLBACK, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 62}}, {CHECK_SNMP_VSMHPORT, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 63}}, {CHECK_SNMP_VSDELAYLOOPUSEC, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 64}}, {CHECK_SNMP_VSDELAYBEFORERETRYUSEC, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 65}}, {CHECK_SNMP_VSWARMUPUSEC, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 66}}, {CHECK_SNMP_VSCONNTIMEOUTUSEC, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 67}}, {CHECK_SNMP_VSTUNNELTYPE, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 68}}, #ifdef _HAVE_IPVS_TUN_TYPE_ {CHECK_SNMP_VSTUNNELPORT, ASN_UNSIGNED, RONLY, check_snmp_virtualserver, 3, {3, 1, 69}}, #endif #ifdef _HAVE_IPVS_TUN_CSUM_ {CHECK_SNMP_VSTUNNELCSUM, ASN_INTEGER, RONLY, check_snmp_virtualserver, 3, {3, 1, 70}}, #endif {CHECK_SNMP_VSNAME, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 71}}, {CHECK_SNMP_VSQUORUMUPPATH, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 72}}, {CHECK_SNMP_VSQUORUMDOWNPATH, ASN_OCTET_STR, RONLY, check_snmp_virtualserver, 3, {3, 1, 73}}, #ifdef _WITH_LVS_64BIT_STATS_ {CHECK_SNMP_VSRATECPS64, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 74}}, {CHECK_SNMP_VSRATEINPPS64, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 75}}, {CHECK_SNMP_VSRATEOUTPPS64, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 76}}, {CHECK_SNMP_VSRATEINBPS64, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 77}}, {CHECK_SNMP_VSRATEOUTBPS64, ASN_COUNTER64, RONLY, check_snmp_virtualserver, 3, {3, 1, 78}}, #endif /* realServerTable */ {CHECK_SNMP_RSTYPE, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 2}}, {CHECK_SNMP_RSADDRTYPE, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 3}}, {CHECK_SNMP_RSADDRESS, ASN_OCTET_STR, RONLY, check_snmp_realserver, 3, {4, 1, 4}}, {CHECK_SNMP_RSPORT, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 5}}, {CHECK_SNMP_RSSTATUS, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 6}}, {CHECK_SNMP_RSWEIGHT, ASN_INTEGER, RWRITE, check_snmp_realserver, 3, {4, 1, 7}}, {CHECK_SNMP_RSUPPERCONNECTIONLIMIT, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 8}}, {CHECK_SNMP_RSLOWERCONNECTIONLIMIT, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 9}}, {CHECK_SNMP_RSACTIONWHENDOWN, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 10}}, {CHECK_SNMP_RSNOTIFYUP, ASN_OCTET_STR, RONLY, check_snmp_realserver, 3, {4, 1, 11}}, {CHECK_SNMP_RSNOTIFYDOWN, ASN_OCTET_STR, RONLY, check_snmp_realserver, 3, {4, 1, 12}}, {CHECK_SNMP_RSFAILEDCHECKS, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 13}}, {CHECK_SNMP_RSSTATSCONNS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 14}}, {CHECK_SNMP_RSSTATSACTIVECONNS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 15}}, {CHECK_SNMP_RSSTATSINACTIVECONNS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 16}}, {CHECK_SNMP_RSSTATSPERSISTENTCONNS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 17}}, {CHECK_SNMP_RSSTATSINPKTS, ASN_COUNTER, RONLY, check_snmp_realserver, 3, {4, 1, 18}}, {CHECK_SNMP_RSSTATSOUTPKTS, ASN_COUNTER, RONLY, check_snmp_realserver, 3, {4, 1, 19}}, {CHECK_SNMP_RSSTATSINBYTES, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 20}}, {CHECK_SNMP_RSSTATSOUTBYTES, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 21}}, {CHECK_SNMP_RSRATECPS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 22}}, {CHECK_SNMP_RSRATEINPPS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 23}}, {CHECK_SNMP_RSRATEOUTPPS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 24}}, {CHECK_SNMP_RSRATEINBPS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 25}}, {CHECK_SNMP_RSRATEOUTBPS, ASN_GAUGE, RONLY, check_snmp_realserver, 3, {4, 1, 26}}, #ifdef _WITH_LVS_64BIT_STATS_ {CHECK_SNMP_RSSTATSCONNS64, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 27}}, {CHECK_SNMP_RSSTATSINPKTS64, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 28}}, {CHECK_SNMP_RSSTATSOUTPKTS64, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 29}}, /* See below for RSRATECPS64 64 bit counters for rates */ {CHECK_SNMP_RSRATECPSLOW, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 30}}, {CHECK_SNMP_RSRATECPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 31}}, {CHECK_SNMP_RSRATEINPPSLOW, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 32}}, {CHECK_SNMP_RSRATEINPPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 33}}, {CHECK_SNMP_RSRATEOUTPPSLOW, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 34}}, {CHECK_SNMP_RSRATEOUTPPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 35}}, {CHECK_SNMP_RSRATEINBPSLOW, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 36}}, {CHECK_SNMP_RSRATEINBPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 37}}, {CHECK_SNMP_RSRATEOUTBPSLOW, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 38}}, {CHECK_SNMP_RSRATEOUTBPSHIGH, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 39}}, #endif {CHECK_SNMP_RSLOADBALANCINGKIND, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 40}}, {CHECK_SNMP_RSVIRTUALHOST, ASN_OCTET_STR, RONLY, check_snmp_realserver, 3, {4, 1, 41}}, {CHECK_SNMP_RSALPHA, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 42}}, {CHECK_SNMP_RSRETRY, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 43}}, {CHECK_SNMP_RSDELAYBEFORERETRY, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 44}}, {CHECK_SNMP_RSWARMUP, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 45}}, {CHECK_SNMP_RSDELAYLOOP, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 46}}, {CHECK_SNMP_RSSMTPALERT, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 47}}, {CHECK_SNMP_RSDELAYBEFORERETRYUSEC, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 48}}, {CHECK_SNMP_RSWARMUPUSEC, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 49}}, {CHECK_SNMP_RSDELAYLOOPUSEC, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 50}}, {CHECK_SNMP_RSCONNTIMEOUTUSEC, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 51}}, {CHECK_SNMP_RSTUNNELTYPE, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 52}}, #ifdef _HAVE_IPVS_TUN_TYPE_ {CHECK_SNMP_RSTUNNELPORT, ASN_UNSIGNED, RONLY, check_snmp_realserver, 3, {4, 1, 53}}, #endif #ifdef _HAVE_IPVS_TUN_CSUM_ {CHECK_SNMP_RSTUNNELCSUM, ASN_INTEGER, RONLY, check_snmp_realserver, 3, {4, 1, 54}}, #endif {CHECK_SNMP_RSNAME, ASN_OCTET_STR, RONLY, check_snmp_realserver, 3, {4, 1, 55}}, {CHECK_SNMP_RSNOTIFYUPPATH, ASN_OCTET_STR, RONLY, check_snmp_realserver, 3, {4, 1, 56}}, {CHECK_SNMP_RSNOTIFYDOWNPATH, ASN_OCTET_STR, RONLY, check_snmp_realserver, 3, {4, 1, 57}}, #ifdef _WITH_LVS_64BIT_STATS_ {CHECK_SNMP_RSRATECPS64, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 58}}, {CHECK_SNMP_RSRATEINPPS64, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 59}}, {CHECK_SNMP_RSRATEOUTPPS64, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 60}}, {CHECK_SNMP_RSRATEINBPS64, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 61}}, {CHECK_SNMP_RSRATEOUTBPS64, ASN_COUNTER64, RONLY, check_snmp_realserver, 3, {4, 1, 62}}, #endif #ifdef _WITH_VRRP_ /* LVS sync daemon configuration */ {CHECK_SNMP_LVSSYNCDAEMONENABLED, ASN_INTEGER, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 1}}, {CHECK_SNMP_LVSSYNCDAEMONINTERFACE, ASN_OCTET_STR, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 2}}, {CHECK_SNMP_LVSSYNCDAEMONVRRPINSTANCE, ASN_OCTET_STR, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 3}}, {CHECK_SNMP_LVSSYNCDAEMONSYNCID, ASN_INTEGER, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 4}}, #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ {CHECK_SNMP_LVSSYNCDAEMONMAXLEN, ASN_INTEGER, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 5}}, {CHECK_SNMP_LVSSYNCDAEMONPORT, ASN_INTEGER, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 6}}, {CHECK_SNMP_LVSSYNCDAEMONTTL, ASN_INTEGER, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 7}}, {CHECK_SNMP_LVSSYNCDAEMONMCASTGROUPADDRTYPE, ASN_INTEGER, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 8}}, {CHECK_SNMP_LVSSYNCDAEMONMCASTGROUPADDRVALUE, ASN_OCTET_STR, RONLY, check_snmp_lvs_sync_daemon, 2, {6, 9}}, #endif #endif /* LVS timeouts */ {CHECK_SNMP_LVSTIMEOUTTCP, ASN_INTEGER, RONLY, check_snmp_lvs_timeouts, 2, {7, 1}}, {CHECK_SNMP_LVSTIMEOUTTCPFIN, ASN_INTEGER, RONLY, check_snmp_lvs_timeouts, 2, {7, 2}}, {CHECK_SNMP_LVSTIMEOUTUDP, ASN_INTEGER, RONLY, check_snmp_lvs_timeouts, 2, {7, 3}}, }; void check_snmp_agent_init(const char *snmp_socket) { if (snmp_running) return; /* We handle the global oid if we are running SNMP */ snmp_agent_init(snmp_socket, true); snmp_register_mib(check_oid, OID_LENGTH(check_oid), "Healthchecker", PTR_CAST(struct variable, check_vars), sizeof(check_vars[0]), sizeof(check_vars)/sizeof(check_vars[0])); } void check_snmp_agent_close(void) { if (!snmp_running) return; snmp_unregister_mib(check_oid, OID_LENGTH(check_oid)); snmp_agent_close(true); } void check_snmp_rs_trap(real_server_t *rs, virtual_server_t *vs, bool stopping) { real_server_t *r; snmp_ret_t ptr_conv; /* OID of the notification */ oid notification_oid[] = { CHECK_OID, 5, 0, 1 }; size_t notification_oid_len = OID_LENGTH(notification_oid); /* OID for snmpTrapOID.0 */ oid objid_snmptrap[] = { SNMPTRAP_OID }; size_t objid_snmptrap_len = OID_LENGTH(objid_snmptrap); /* Other OID */ oid addrtype_oid[] = { CHECK_OID, 4, 1, 3 }; size_t addrtype_oid_len = OID_LENGTH(addrtype_oid); static unsigned long addrtype = 1; oid address_oid[] = { CHECK_OID, 4, 1, 4 }; size_t address_oid_len = OID_LENGTH(address_oid); oid port_oid[] = { CHECK_OID, 4, 1, 5 }; size_t port_oid_len = OID_LENGTH(port_oid); static unsigned long port; oid status_oid[] = { CHECK_OID, 4, 1, 6 }; size_t status_oid_len = OID_LENGTH(status_oid); static unsigned long status; oid vstype_oid[] = { CHECK_OID, 3, 1, 2 }; size_t vstype_oid_len = OID_LENGTH(vstype_oid); static unsigned long vstype; oid vsgroupname_oid[] = { CHECK_OID, 3, 1, 3 }; size_t vsgroupname_oid_len = OID_LENGTH(vsgroupname_oid); oid vsfwmark_oid[] = { CHECK_OID, 3, 1, 4 }; size_t vsfwmark_oid_len = OID_LENGTH(vsfwmark_oid); static unsigned long vsfwmark; oid vsaddrtype_oid[] = {CHECK_OID, 3, 1, 5 }; size_t vsaddrtype_oid_len = OID_LENGTH(vsaddrtype_oid); oid vsaddress_oid[] = {CHECK_OID, 3, 1, 6 }; size_t vsaddress_oid_len = OID_LENGTH(vsaddress_oid); oid vsport_oid[] = {CHECK_OID, 3, 1, 7 }; size_t vsport_oid_len = OID_LENGTH(vsport_oid); static unsigned long vsport; oid vsprotocol_oid[] = {CHECK_OID, 3, 1, 8 }; size_t vsprotocol_oid_len = OID_LENGTH(vsprotocol_oid); static unsigned long vsprotocol; oid realup_oid[] = {CHECK_OID, 3, 1, 21 }; size_t realup_oid_len = OID_LENGTH(realup_oid); static unsigned long realup; oid realtotal_oid[] = {CHECK_OID, 3, 1, 20 }; size_t realtotal_oid_len = OID_LENGTH(realtotal_oid); static unsigned long realtotal; oid quorumstatus_oid[] = {CHECK_OID, 3, 1, 23 }; size_t quorumstatus_oid_len = OID_LENGTH(quorumstatus_oid); static unsigned long quorumstatus; oid quorum_oid[] = {CHECK_OID, 3, 1, 22 }; size_t quorum_oid_len = OID_LENGTH(quorum_oid); static unsigned long quorum; oid routerId_oid[] = { KEEPALIVED_OID, 1, 2, 0 }; size_t routerId_oid_len = OID_LENGTH(routerId_oid); netsnmp_variable_list *notification_vars = NULL; if (!global_data->enable_traps) return; if (!rs) notification_oid[notification_oid_len - 1] = 2; /* Initialize data */ realtotal = vs->rs_cnt; realup = 0; list_for_each_entry(r, &vs->rs, e_list) if (r->alive) realup++; /* snmpTrapOID */ snmp_varlist_add_variable(¬ification_vars, objid_snmptrap, objid_snmptrap_len, ASN_OBJECT_ID, PTR_CAST(u_char, notification_oid), notification_oid_len * sizeof(oid)); if (rs) { /* realServerAddrType */ addrtype = SNMP_InetAddressType(rs->addr.ss_family); snmp_varlist_add_variable(¬ification_vars, addrtype_oid, addrtype_oid_len, ASN_INTEGER, PTR_CAST(u_char, &addrtype), sizeof(addrtype)); /* realServerAddress */ snmp_varlist_add_variable(¬ification_vars, address_oid, address_oid_len, ASN_OCTET_STR, (rs->addr.ss_family == AF_INET6)? PTR_CAST2(u_char, struct sockaddr_in6, &rs->addr, sin6_addr): PTR_CAST2(u_char, struct sockaddr_in, &rs->addr, sin_addr), (rs->addr.ss_family == AF_INET6) ? 16 : 4); /* realServerPort */ port = htons(inet_sockaddrport(&rs->addr)); snmp_varlist_add_variable(¬ification_vars, port_oid, port_oid_len, ASN_UNSIGNED, PTR_CAST(u_char, &port), sizeof(port)); /* realServerStatus */ status = SNMP_TruthValue(rs->alive); snmp_varlist_add_variable(¬ification_vars, status_oid, status_oid_len, ASN_INTEGER, PTR_CAST(u_char, &status), sizeof(status)); } /* virtualServerType */ if (vs->vsgname) vstype = 3; else if (vs->vfwmark) vstype = 1; else vstype = 2; snmp_varlist_add_variable(¬ification_vars, vstype_oid, vstype_oid_len, ASN_INTEGER, PTR_CAST(u_char, &vstype), sizeof(vstype)); if (vs->vsgname) { /* virtualServerNameOfGroup */ snmp_varlist_add_variable(¬ification_vars, vsgroupname_oid, vsgroupname_oid_len, ASN_OCTET_STR, PTR_CAST_CONST(u_char, vs->vsgname), strlen(vs->vsgname)); } else if (vs->vfwmark) { vsfwmark = vs->vfwmark; snmp_varlist_add_variable(¬ification_vars, vsfwmark_oid, vsfwmark_oid_len, ASN_UNSIGNED, PTR_CAST(u_char, &vsfwmark), sizeof(vsfwmark)); } else { addrtype = SNMP_InetAddressType(vs->addr.ss_family); snmp_varlist_add_variable(¬ification_vars, vsaddrtype_oid, vsaddrtype_oid_len, ASN_INTEGER, PTR_CAST(u_char, &addrtype), sizeof(addrtype)); snmp_varlist_add_variable(¬ification_vars, vsaddress_oid, vsaddress_oid_len, ASN_OCTET_STR, (vs->addr.ss_family == AF_INET6) ? PTR_CAST2(u_char, struct sockaddr_in6, &vs->addr, sin6_addr) : PTR_CAST2(u_char, struct sockaddr_in, &vs->addr, sin_addr), (vs->addr.ss_family == AF_INET6) ? 16 : 4); vsport = htons(inet_sockaddrport(&vs->addr)); snmp_varlist_add_variable(¬ification_vars, vsport_oid, vsport_oid_len, ASN_UNSIGNED, PTR_CAST(u_char, &vsport), sizeof(vsport)); } vsprotocol = SNMP_TruthValue(vs->service_type == IPPROTO_TCP); snmp_varlist_add_variable(¬ification_vars, vsprotocol_oid, vsprotocol_oid_len, ASN_INTEGER, PTR_CAST(u_char, &vsprotocol), sizeof(vsprotocol)); if (!rs) { quorumstatus = stopping ? 3 : vs->quorum_state_up ? 1 : 2; snmp_varlist_add_variable(¬ification_vars, quorumstatus_oid, quorumstatus_oid_len, ASN_INTEGER, PTR_CAST(u_char, &quorumstatus), sizeof(quorumstatus)); quorum = vs->quorum; snmp_varlist_add_variable(¬ification_vars, quorum_oid, quorum_oid_len, ASN_UNSIGNED, PTR_CAST(u_char, &quorum), sizeof(quorum)); } snmp_varlist_add_variable(¬ification_vars, realup_oid, realup_oid_len, ASN_UNSIGNED, PTR_CAST(u_char, &realup), sizeof(realup)); snmp_varlist_add_variable(¬ification_vars, realtotal_oid, realtotal_oid_len, ASN_UNSIGNED, PTR_CAST(u_char, &realtotal), sizeof(realtotal)); /* routerId */ ptr_conv.cp = global_data->router_id, snmp_varlist_add_variable(¬ification_vars, routerId_oid, routerId_oid_len, ASN_OCTET_STR, ptr_conv.p, strlen(global_data->router_id)); send_v2trap(notification_vars); snmp_free_varbind(notification_vars); } void check_snmp_quorum_trap(virtual_server_t *vs, bool stopping) { check_snmp_rs_trap(NULL, vs, stopping); } keepalived-2.3.3/keepalived/check/check_parser.c0000664000175000017500000007565314705542521015323 /* * Soft: Keepalived is a failover program for the LVS project * . 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, * * 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, */ #include "config.h" #include #include #include #include #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", "twos", NULL}; virtual_server_t *current_vs; real_server_t *current_rs; virtual_server_group_t *current_vsg; /* 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 handle_ssl_file(const vector_t *strvec, const char **file_name, const char *type) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "SSL %s missing", type); return; } set_string(file_name, strvec, "SSL"); } static void sslpass_handler(const vector_t *strvec) { handle_ssl_file(strvec, &check_data->ssl->password, "password"); } static void sslca_handler(const vector_t *strvec) { handle_ssl_file(strvec, &check_data->ssl->cafile, "cafile"); } static void sslcert_handler(const vector_t *strvec) { handle_ssl_file(strvec, &check_data->ssl->certfile, "certfile"); } static void sslkey_handler(const vector_t *strvec) { handle_ssl_file(strvec, &check_data->ssl->keyfile, "keyfile"); } /* Virtual Servers handlers */ static void vsg_handler(const vector_t *strvec) { vector_t *my_strvec; ptr_hack_t word; unsigned i; if (!strvec) return; /* Fetch queued vsg */ current_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 */ if (list_empty(¤t_vsg->vfwmark) && list_empty(¤t_vsg->addr_range)) { report_config_error(CONFIG_GENERAL_ERROR, "virtual server group %s has no entries - removing" , current_vsg->gname); free_vsg(current_vsg); return; } else if (current_vsg->have_ipv4 && current_vsg->have_ipv6 && current_vsg->fwmark_no_family) { /* The error here is fwmark_no_family && all rs tunnelled - but we only know that later */ report_config_error(CONFIG_GENERAL_ERROR, "virtual server group %s cannot have IPv4, IPv6" " and fwmark without family - removing" , current_vsg->gname); free_vsg(current_vsg); return; } list_add_tail(¤t_vsg->e_list, &check_data->vs_group); current_vsg = NULL; } 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; current_vs = alloc_vs(strvec_slot(strvec, 1), vector_size(strvec) >= 3 ? strvec_slot(strvec, 2) : NULL); } static void vs_end_handler(void) { real_server_t *rs; bool mixed_af; if (list_empty(¤t_vs->rs)) { report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s has no real servers - ignoring", FMT_VS(current_vs)); free_vs(current_vs); return; } /* If the real (sorry) server uses tunnel forwarding, the address family * does not have to match the address family of the virtual server */ if (current_vs->s_svr #if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY && current_vs->s_svr->forwarding_method != IP_VS_CONN_F_TUNNEL #endif ) { if (current_vs->af == AF_UNSPEC) current_vs->af = current_vs->s_svr->addr.ss_family; else if (current_vs->af != current_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(¤t_vs->s_svr->addr)); FREE(current_vs->s_svr); current_vs->s_svr = NULL; } } if (current_vs->af == AF_UNSPEC && !current_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 (current_vs->s_svr) current_vs->af = current_vs->s_svr->addr.ss_family; list_for_each_entry(rs, ¤t_vs->rs, e_list) { if (current_vs->af == AF_UNSPEC) current_vs->af = rs->addr.ss_family; else if (current_vs->af != rs->addr.ss_family) { mixed_af = true; break; } } if (mixed_af || current_vs->af == AF_UNSPEC) { /* We have a mixture of IPv4 and IPv6 tunnelled real/sorry servers. * Default to IPv4. */ current_vs->af = AF_INET; } } list_add_tail(¤t_vs->e_list, &check_data->vs); } static void ip_family_handler(const vector_t *strvec) { 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); free_vs(current_vs); current_vs = NULL; return; #endif /* coverity[unreachable] */ af = AF_INET6; } else { report_config_error(CONFIG_GENERAL_ERROR, "unknown address family %s", strvec_slot(strvec, 1)); return; } if (current_vs->af != AF_UNSPEC && af != current_vs->af) { report_config_error(CONFIG_GENERAL_ERROR, "Virtual server specified family %s conflicts with server family", strvec_slot(strvec, 1)); return; } current_vs->af = af; } static void vs_co_timeout_handler(const vector_t *strvec) { 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; } current_vs->connection_to = timer; } static void vs_delay_handler(const vector_t *strvec) { unsigned long delay; if (read_timer(strvec, 1, &delay, 1, 0, true)) current_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) { unsigned long delay; if (read_timer(strvec, 1, &delay, 0, 0, true)) current_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) { 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; } current_vs->retry = retry; } static void vs_warmup_handler(const vector_t *strvec) { unsigned long delay; if (read_timer(strvec, 1, &delay, 0, 0, true)) current_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) { 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(current_vs->sched)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_scheduler '%s' - ignoring", strvec_slot(strvec, 1)); return; } strcpy(current_vs->sched, str); } static void lbflags_handler(const vector_t *strvec) { const char *str = strvec_slot(strvec, 0); if (!strcmp(str, "ops")) current_vs->flags |= IP_VS_SVC_F_ONEPACKET; #ifdef IP_VS_SVC_F_SCHED1 /* From Linux 3.11 */ else if (!strcmp(str, "flag-1")) current_vs->flags |= IP_VS_SVC_F_SCHED1; else if (!strcmp(str, "flag-2")) current_vs->flags |= IP_VS_SVC_F_SCHED2; else if (!strcmp(str, "flag-3")) current_vs->flags |= IP_VS_SVC_F_SCHED3; else if (!strcmp(current_vs->sched , "sh") ) { /* sh-port and sh-fallback flags are relevant for sh scheduler only */ if (!strcmp(str, "sh-port") ) current_vs->flags |= IP_VS_SVC_F_SCHED_SH_PORT; if (!strcmp(str, "sh-fallback")) current_vs->flags |= IP_VS_SVC_F_SCHED_SH_FALLBACK; } else if (!strcmp(current_vs->sched , "mh") ) { /* mh-port and mh-fallback flags are relevant for mh scheduler only */ if (!strcmp(str, "mh-port") ) current_vs->flags |= IP_VS_SVC_F_SCHED_MH_PORT; if (!strcmp(str, "mh-fallback")) current_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) { real_server_t rs = { // dummy for setting parameters. Ensure previous values are preserved #ifdef _HAVE_IPVS_TUN_TYPE_ .tun_type = current_vs->tun_type, .tun_port = current_vs->tun_port, #ifdef _HAVE_IPVS_TUN_CSUM_ .tun_flags = current_vs->tun_flags, #endif #endif .forwarding_method = current_vs->forwarding_method }; svr_forwarding_handler(&rs, strvec, "virtual"); current_vs->forwarding_method = rs.forwarding_method; #ifdef _HAVE_IPVS_TUN_TYPE_ current_vs->tun_type = rs.tun_type; current_vs->tun_port = rs.tun_port; #ifdef _HAVE_IPVS_TUN_CSUM_ current_vs->tun_flags = rs.tun_flags; #endif #endif } static void pto_handler(const vector_t *strvec) { unsigned timeout; if (vector_size(strvec) < 2) { current_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; } current_vs->persistence_timeout = (uint32_t)timeout; } static void pengine_handler(const vector_t *strvec) { const char *str = strvec_slot(strvec, 1); size_t size = sizeof (current_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(current_vs->pe_name, str, size - 1); current_vs->pe_name[size - 1] = '\0'; } static void pgr_handler(const vector_t *strvec) { uint16_t af = current_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; } current_vs->persistence_granularity = granularity; } else { struct addrinfo hints = { .ai_flags = AI_NUMERICHOST, .ai_family = AF_INET }; struct addrinfo *res; if (getaddrinfo(strvec_slot(strvec, 1), NULL, &hints, &res)) { 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 /* On 32 bit systems, gcc produces a warning : cast increases required alignment of target type [-Wcast-align] * for * current_vs->persistence_granularity = ((struct sockaddr_in *)res->ai_addr)->sin_addr.s_addr; */ union { struct sockaddr sa; struct sockaddr_in sa_in; } sa; sa.sa = *res->ai_addr; current_vs->persistence_granularity = sa.sa_in.sin_addr.s_addr; freeaddrinfo(res); } if (current_vs->af == AF_UNSPEC) current_vs->af = af; if (!current_vs->persistence_timeout) current_vs->persistence_timeout = IPVS_SVC_PERSISTENT_TIMEOUT; } static void proto_handler(const vector_t *strvec) { const char *str = strvec_slot(strvec, 1); if (!strcasecmp(str, "TCP")) current_vs->service_type = IPPROTO_TCP; else if (!strcasecmp(str, "SCTP")) current_vs->service_type = IPPROTO_SCTP; else if (!strcasecmp(str, "UDP")) current_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) { current_vs->ha_suspend = true; } static void vs_smtp_alert_handler(const vector_t *strvec) { 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; } } current_vs->smtp_alert = res; check_data->num_smtp_alert++; } static void vs_virtualhost_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "virtual server virtualhost missing"); return; } set_string(¤t_vs->virtualhost, strvec, "vs virtualhost"); } #ifdef _WITH_SNMP_CHECKER_ static void vs_snmp_name_handler(const vector_t *strvec) { if (vector_size(strvec) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "virtual server snmp_name missing or extra parameters"); return; } set_string(¤t_vs->snmp_name, strvec, "vs snmp_name"); } #endif /* 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) { if (current_vs->s_svr) current_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) { if (current_vs->s_svr) svr_forwarding_handler(current_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) { current_rs = alloc_rs(strvec_slot(strvec, 1), vector_size(strvec) >= 3 ? strvec_slot(strvec, 2) : NULL); } static void rs_end_handler(void) { /* 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 (current_rs->forwarding_method != IP_VS_CONN_F_TUNNEL) #endif { if (current_vs->af == AF_UNSPEC) current_vs->af = current_rs->addr.ss_family; else if (current_vs->af != current_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(¤t_rs->addr)); FREE(current_rs); return; } } list_add_tail(¤t_rs->e_list, ¤t_vs->rs); #ifdef _WITH_SNMP_CHECKER_ current_vs->rs_cnt++; #endif } static void rs_weight_handler(const vector_t *strvec) { unsigned weight; if (!read_unsigned_strvec(strvec, 1, &weight, 0, IPVS_WEIGHT_LIMIT, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Real server weight %s is outside range 0-%d", strvec_slot(strvec, 1), IPVS_WEIGHT_LIMIT); return; } current_rs->effective_weight = weight; current_rs->iweight = weight; } static void rs_forwarding_handler(const vector_t *strvec) { svr_forwarding_handler(current_rs, strvec, "real"); } static void uthreshold_handler(const vector_t *strvec) { 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; } current_rs->u_threshold = threshold; } static void lthreshold_handler(const vector_t *strvec) { 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; } current_rs->l_threshold = threshold; } static void vs_inhibit_handler(__attribute__((unused)) const vector_t *strvec) { current_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) { if (current_rs->notify_up) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_up script already specified - ignoring %s", current_vs->vsgname, strvec_slot(strvec,1)); return; } current_rs->notify_up = set_check_notify_script(strvec, "notify"); } static void notify_down_handler(const vector_t *strvec) { if (current_rs->notify_down) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_down script already specified - ignoring %s", current_vs->vsgname, strvec_slot(strvec,1)); return; } current_rs->notify_down = set_check_notify_script(strvec, "notify"); } static void rs_co_timeout_handler(const vector_t *strvec) { 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; } current_rs->connection_to = timer; } static void rs_delay_handler(const vector_t *strvec) { unsigned long delay; if (read_timer(strvec, 1, &delay, 1, 0, true)) current_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) { unsigned long delay; if (read_timer(strvec, 1, &delay, 0, 0, true)) current_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) { 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; } current_rs->retry = (unsigned)retry; } static void rs_warmup_handler(const vector_t *strvec) { unsigned long delay; if (read_timer(strvec, 1, &delay, 0, 0, true)) current_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) { 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; } } current_rs->inhibit = res; } static void rs_alpha_handler(const vector_t *strvec) { 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; } } current_rs->alpha = res; } static void rs_smtp_alert_handler(const vector_t *strvec) { 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; } } current_rs->smtp_alert = res; check_data->num_smtp_alert++; } static void rs_virtualhost_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "real server virtualhost missing"); return; } set_string(¤t_rs->virtualhost, strvec, "rs virtualhost"); } #ifdef _WITH_SNMP_CHECKER_ static void rs_snmp_name_handler(const vector_t *strvec) { if (vector_size(strvec) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "real server snmp_name missing or extra parameters"); return; } set_string(¤t_rs->snmp_name, strvec, "rs snmp_name"); } #endif static void vs_alpha_handler(__attribute__((unused)) const vector_t *strvec) { current_vs->alpha = true; } static void omega_handler(__attribute__((unused)) const vector_t *strvec) { current_vs->omega = true; } static void quorum_up_handler(const vector_t *strvec) { if (current_vs->notify_quorum_up) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) quorum_up script already specified - ignoring %s", current_vs->vsgname, strvec_slot(strvec,1)); return; } current_vs->notify_quorum_up = set_check_notify_script(strvec, "quorum"); } static void quorum_down_handler(const vector_t *strvec) { if (current_vs->notify_quorum_down) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) quorum_down script already specified - ignoring %s", current_vs->vsgname, strvec_slot(strvec,1)); return; } current_vs->notify_quorum_down = set_check_notify_script(strvec, "quorum"); } static void quorum_handler(const vector_t *strvec) { 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; } current_vs->quorum = quorum; } static void hysteresis_handler(const vector_t *strvec) { 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; } current_vs->hysteresis = hysteresis; } static void vs_weight_handler(const vector_t *strvec) { unsigned weight; if (!read_unsigned_strvec(strvec, 1, &weight, 1, IPVS_WEIGHT_LIMIT, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Virtual server weight %s is outside range 1-%d", strvec_slot(strvec, 1), IPVS_WEIGHT_LIMIT); return; } current_vs->weight = weight; } #ifdef _WITH_SANITIZE_UNDEFINED_ __attribute__((no_sanitize("null"))) #endif void init_check_keywords(bool active) { vpp_t check_ptr; /* SSL mapping */ install_keyword_root("SSL", &ssl_handler, active, VPP &check_data->ssl); // Causes sanitizer error: member access within null pointer of type 'struct check_data_t' 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, NULL); install_keyword_root("virtual_server", &vs_handler, active, VPP ¤t_vs); install_level_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); install_keyword("ops", &lbflags_handler); #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); install_keyword("persistence_engine", &pengine_handler); 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); #ifdef _WITH_SNMP_CHECKER_ install_keyword("snmp_name", &vs_snmp_name_handler); #endif /* Pool regression detection and handling. */ install_keyword("alpha", &vs_alpha_handler); install_keyword("omega", &omega_handler); install_keyword_quoted("quorum_up", &quorum_up_handler); install_keyword_quoted("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); check_ptr = install_sublevel(VPP ¤t_rs); 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_quoted("notify_up", ¬ify_up_handler); install_keyword_quoted("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); #ifdef _WITH_SNMP_CHECKER_ install_keyword("snmp_name", &rs_snmp_name_handler); #endif install_level_end_handler(&rs_end_handler); /* Checkers mapping */ install_checkers_keyword(); install_sublevel_end(check_ptr); } 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; } keepalived-2.3.3/keepalived/check/check_smtp.c0000664000175000017500000006204714661620303014777 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: SMTP CHECK. Check an SMTP-server. * * Authors: Jeremy Rumpf, * Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include "check_smtp.h" #include "logger.h" #include "ipwrapper.h" #include "utils.h" #include "parser.h" #include "layer4.h" #include "smtp.h" #ifdef THREAD_DUMP #include "scheduler.h" #endif #include "check_parser.h" /* Specifying host blocks within the SMTP checker is deprecated, but currently * still supported. All code to support it is in WITH_HOST_ENTRIES conditional * compilation, so it is easy to remove all the code eventually. */ #define WITH_HOST_ENTRIES #ifdef WITH_HOST_ENTRIES static LIST_HEAD_INITIALIZE(host_list); /* ref_co_t */ typedef struct _ref_co { conn_opts_t *co; /* Linked list member */ list_head_t e_list; } ref_co_t; static checker_t *current_checker_host; #endif static void smtp_connect_thread(thread_ref_t); static void smtp_start_check_thread(thread_ref_t); static void smtp_engine_thread(thread_ref_t); /* Used as a callback from the checker api, queue_checker(), * to free up a checker entry and all its associated data. */ static void free_smtp_check(checker_t *checker) { smtp_checker_t *smtp_checker = checker->data; FREE_PTR(checker->co); FREE_CONST(smtp_checker->helo_name); FREE(smtp_checker); FREE(checker); } /* * Callback for whenever we've been requested to dump our * configuration. */ static void dump_smtp_check(FILE *fp, const checker_t *checker) { const smtp_checker_t *smtp_checker = checker->data; conf_write(fp, " Keepalive method = SMTP_CHECK"); conf_write(fp, " helo = %s", smtp_checker->helo_name); } static bool compare_smtp_check(const checker_t *old_c, checker_t *new_c) { const smtp_checker_t *old = old_c->data; const smtp_checker_t *new = new_c->data; if (strcmp(old->helo_name, new->helo_name) != 0) return false; if (!compare_conn_opts(old_c->co, new_c->co)) return false; return true; } static const checker_funcs_t smtp_checker_funcs = { CHECKER_SMTP, free_smtp_check, dump_smtp_check, compare_smtp_check, NULL }; /* * Callback for whenever an SMTP_CHECK keyword is encountered * in the config file. */ static void smtp_check_handler(__attribute__((unused)) const vector_t *strvec) { smtp_checker_t *smtp_checker; conn_opts_t *co; PMALLOC(smtp_checker); PMALLOC(co); co->connection_to = UINT_MAX; /* Have the checker queue code put our checker into the real server's checkers_queue list. */ queue_checker(&smtp_checker_funcs, smtp_start_check_thread, smtp_checker, co, true); /* We need to be able to check if anything has been set */ co->dst.ss_family = AF_UNSPEC; PTR_CAST(struct sockaddr_in, &co->dst)->sin_port = 0; } static void smtp_check_end_handler(void) { smtp_checker_t *smtp_checker = current_checker->data; checker_t *checker = current_checker; #ifdef WITH_HOST_ENTRIES smtp_checker_t *new_smtp_checker; conn_opts_t *co; ref_co_t *rco, *rco_tmp; list_head_t sav_rs_list; #endif if (!smtp_checker->helo_name) smtp_checker->helo_name = STRDUP(SMTP_DEFAULT_HELO); #ifdef WITH_HOST_ENTRIES if (!list_empty(&host_list)) log_message(LOG_INFO, "The SMTP_CHECK host block is deprecated. Please define additional checkers."); #endif /* If any connection component has been configured, or there are no (deprecated) host entries, * we want to use any information provided, using defaults as necessary. */ #ifdef WITH_HOST_ENTRIES /* Have any of the connection parameters been set, or are there no hosts? */ if (current_checker->co->dst.ss_family != AF_UNSPEC || PTR_CAST(struct sockaddr_in, ¤t_checker->co->dst)->sin_port || current_checker->co->bindto.ss_family != AF_UNSPEC || PTR_CAST(struct sockaddr_in, ¤t_checker->co->bindto)->sin_port || current_checker->co->bind_if[0] || list_empty(&host_list) || #ifdef _WITH_SO_MARK_ current_checker->co->fwmark || #endif current_checker->co->connection_to != UINT_MAX) #endif { /* Set any necessary defaults. NOTE: we are relying on * struct sockaddr_in and sockaddr_in6 port offsets being the same. */ uint16_t saved_port = PTR_CAST(struct sockaddr_in, ¤t_checker->co->dst)->sin_port; if (current_checker->co->dst.ss_family == AF_UNSPEC) { current_checker->co->dst = current_rs->addr; if (saved_port) checker_set_dst_port(¤t_checker->co->dst, saved_port); } if (!saved_port) checker_set_dst_port(¤t_checker->co->dst, PTR_CAST(struct sockaddr_in, ¤t_rs->addr)->sin_port); if (!check_conn_opts(current_checker->co)) { dequeue_new_checker(); return; } } #ifdef WITH_HOST_ENTRIES else { /* No connection options have been specified, but there * is at least one host entry. Use that host entry's * connection options for the main checker. */ FREE(current_checker->co); rco = list_first_entry(&host_list, ref_co_t, e_list); current_checker->co = rco->co; list_del_init(&rco->e_list); FREE(rco); } #endif /* Set the connection timeout if not set */ unsigned conn_to = current_rs->connection_to; if (conn_to == UINT_MAX) conn_to = current_vs->connection_to; if (current_checker->co->connection_to == UINT_MAX) current_checker->co->connection_to = conn_to; #ifdef WITH_HOST_ENTRIES /* Create a new checker for each host on the host list */ list_for_each_entry_safe(rco, rco_tmp, &host_list, e_list) { co = rco->co; PMALLOC(new_smtp_checker); *new_smtp_checker = *smtp_checker; if (co->connection_to == UINT_MAX) co->connection_to = conn_to; new_smtp_checker->helo_name = STRDUP(smtp_checker->helo_name); queue_checker(&smtp_checker_funcs, smtp_start_check_thread, new_smtp_checker, NULL, true); /* Copy the checker info, but preserve the list_head entry, th * co pointer and the pointer to new_smtp_checker. */ sav_rs_list = current_checker->rs_list; *current_checker = *checker; current_checker->rs_list = sav_rs_list; current_checker->co = co; current_checker->data = new_smtp_checker; /* queue the checker */ list_add_tail(¤t_checker->rs_list, &checker->rs->checkers_list); list_del_init(&rco->e_list); FREE(rco); } #endif } #ifdef WITH_HOST_ENTRIES /* Callback for "host" keyword */ static void smtp_host_handler(__attribute__((unused)) const vector_t *strvec) { PMALLOC(current_checker_host); PMALLOC(current_checker_host->co); /* Default to the RS */ current_checker_host->co->dst = current_rs->addr; } static void smtp_host_end_handler(void) { ref_co_t *rco; if (!check_conn_opts(current_checker_host->co)) FREE(current_checker_host->co); else { PMALLOC(rco); INIT_LIST_HEAD(&rco->e_list); rco->co = current_checker_host->co; list_add_tail(&rco->e_list, &host_list); } FREE(current_checker_host); } #endif /* "helo_name" keyword */ static void smtp_helo_name_handler(const vector_t *strvec) { smtp_checker_t *smtp_checker = current_checker->data; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "SMTP_CHECK helo name missing"); return; } if (smtp_checker->helo_name) { report_config_error(CONFIG_GENERAL_ERROR, "SMTP_CHECK helo name already specified"); FREE_CONST(smtp_checker->helo_name); } smtp_checker->helo_name = set_value(strvec); } /* Config callback installer */ void install_smtp_check_keyword(void) { vpp_t check_ptr; #ifdef WITH_HOST_ENTRIES vpp_t check_ptr1; #endif /* * Notify the config log parser that we need to be notified via * callbacks when the following keywords are encountered in the * keepalive.conf file. */ install_keyword("SMTP_CHECK", &smtp_check_handler); check_ptr = install_sublevel(VPP ¤t_checker); install_keyword("helo_name", &smtp_helo_name_handler); install_checker_common_keywords(true); /* * The host list feature is deprecated. It makes config fussy by * adding another nesting level and is excessive since it is possible * to attach multiple checkers to a RS. * So these keywords below are kept for compatibility with users' * existing configs. */ #ifdef WITH_HOST_ENTRIES install_keyword("host", &smtp_host_handler); check_ptr1 = install_sublevel(VPP ¤t_checker_host); install_checker_common_keywords(true); install_level_end_handler(smtp_host_end_handler); install_sublevel_end(check_ptr1); #endif install_level_end_handler(&smtp_check_end_handler); install_sublevel_end(check_ptr); } /* * Final handler. Determines if we need a retry or not. * Also has to make a decision if we need to bring the resulting * service down in case of error. */ static int __attribute__ ((format (printf, 2, 3))) smtp_final(thread_ref_t thread, const char *format, ...) { checker_t *checker = THREAD_ARG(thread); char error_buff[512]; char smtp_buff[542]; va_list varg_list; bool checker_was_up; bool rs_was_alive; /* Error or no error we should always have to close the socket */ if (thread->type != THREAD_READY_TIMER) thread_close_fd(thread); if (format) { /* Always syslog the error when the real server is up */ if ((checker->is_up || !checker->has_run) && (global_data->checker_log_all_failures || checker->log_all_failures || checker->retry_it >= checker->retry)) { /* prepend format with the "SMTP_CHECK " string */ strcpy_safe(error_buff, "SMTP_CHECK "); strncat(error_buff, format, sizeof(error_buff) - 11 - 1); va_start(varg_list, format); vlog_message(LOG_INFO, error_buff, varg_list); va_end(varg_list); } /* * If we still have retries left, try this host again by * scheduling the main thread to check it again after the * configured backoff delay. Otherwise down the RS. */ if (++checker->retry_it <= checker->retry) { thread_add_timer(thread->master, smtp_connect_thread, checker, checker->delay_before_retry); return 0; } /* * No more retries, pull the real server from the virtual server. * Only smtp_alert if it wasn't previously down. It should * be noted that smtp_alert makes a copy of the string arguments, so * we don't have to keep them statically allocated. */ if (checker->is_up || !checker->has_run) { checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(DOWN, checker); if (checker->rs->smtp_alert && checker_was_up && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) { if (format != NULL) { snprintf(error_buff, sizeof(error_buff), "=> CHECK failed on service : %s <=", format); va_start(varg_list, format); vsnprintf(smtp_buff, sizeof(smtp_buff), error_buff, varg_list); va_end(varg_list); } else strncpy(smtp_buff, "=> CHECK failed on service <=", sizeof(smtp_buff)); smtp_buff[sizeof(smtp_buff) - 1] = '\0'; smtp_alert(SMTP_MSG_RS, checker, NULL, smtp_buff); } } /* Reschedule the main thread using the configured delay loop */ thread_add_timer(thread->master, smtp_start_check_thread, checker, checker->delay_loop); return 0; } /* * Ok this host was successful, increment to the next host in the list * and reset the retry_it counter. We'll then reschedule the main thread again. * If host_ptr exceeds the end of the list, smtp_connect_main_thread will * take note and bring up the real server as well as inject the delay_loop. */ checker->retry_it = 0; /* * Set the internal host pointer to the host that we'll be * working on. If it's NULL, we've successfully tested all hosts. * We'll bring the service up (if it's not already), reset the host list, * and insert the delay loop. When we get scheduled again the host list * will be reset and we will continue on checking them one by one. */ if (!checker->is_up || !checker->has_run) { log_message(LOG_INFO, "Remote SMTP server %s succeed on service." , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(UP, checker); if (checker->rs->smtp_alert && !checker_was_up && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> CHECK succeed on service <="); } checker->has_run = true; thread_add_timer(thread->master, smtp_start_check_thread, checker, checker->delay_loop); return 0; } /* * One thing to note here is we do a very cheap check for a newline. * We could receive two lines (with two newline characters) in a * single packet, but we don't care. We are only looking at the * SMTP response codes at the beginning anyway. */ static void smtp_get_line_cb(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); smtp_checker_t *smtp_checker = CHECKER_ARG(checker); conn_opts_t *smtp_host = checker->co; ssize_t r; char *nl; /* Handle read timeout */ if (thread->type == THREAD_READ_TIMEOUT) { smtp_final(thread, "Read timeout from server %s" , FMT_SMTP_RS(smtp_host)); return; } /* wrap the buffer, if full, by clearing it */ if (smtp_checker->buff_ctr >= SMTP_BUFF_MAX - 1) { log_message(LOG_INFO, "SMTP_CHECK Buffer overflow reading from server %s. " "Increase SMTP_BUFF_MAX in check_smtp.h" , FMT_SMTP_RS(smtp_host)); smtp_checker->buff_ctr = 0; } /* read the data */ r = read(thread->u.f.fd, smtp_checker->buff + smtp_checker->buff_ctr, SMTP_BUFF_MAX - smtp_checker->buff_ctr - 1); if (r == -1 && (check_EAGAIN(errno) || check_EINTR(errno))) { thread_add_read(thread->master, smtp_get_line_cb, checker, thread->u.f.fd, smtp_host->connection_to, THREAD_DESTROY_CLOSE_FD); return; } /* * If the connection was closed or there was * some sort of error, notify smtp_final() */ if (r <= 0) { smtp_final(thread, "Read failure from server %s" , FMT_SMTP_RS(smtp_host)); return; } smtp_checker->buff_ctr += (size_t)r; smtp_checker->buff[smtp_checker->buff_ctr] = '\0'; /* check if we have a newline, if so, callback */ if ((nl = strchr(smtp_checker->buff, '\n'))) { *nl = '\0'; #ifdef _CHECKER_DEBUG_ if (do_checker_debug) log_message(LOG_DEBUG, "SMTP_CHECK %s < %s" , FMT_SMTP_RS(smtp_host) , smtp_checker->buff); #endif smtp_engine_thread(thread); return; } /* * Last case, we haven't read enough data yet * to pull a newline. Schedule ourselves for * another round. */ thread_add_read(thread->master, smtp_get_line_cb, checker, thread->u.f.fd, smtp_host->connection_to, THREAD_DESTROY_CLOSE_FD); } /* * Ok a caller has asked us to asyncronously schedule a single line * to be received from the server. They have also passed us a call back * function that we'll call once we have the newline. If something bad * happens, the caller assumes we'll pass the error off to smtp_final(), * which will either down the real server or schedule a retry. The * function smtp_get_line_cb is what does the dirty work since the * scheduler can only accept a single *thread argument. */ static void smtp_get_line(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); smtp_checker_t *smtp_checker = CHECKER_ARG(checker); conn_opts_t *smtp_host = checker->co; /* clear the buffer */ smtp_checker->buff_ctr = 0; /* schedule the I/O with our helper function */ thread_add_read(thread->master, smtp_get_line_cb, checker, thread->u.f.fd, smtp_host->connection_to, THREAD_DESTROY_CLOSE_FD); thread_del_write(thread); } /* * The scheduler function that puts the data out on the wire. * All our data will fit into one packet, so we only check if * the current write would block or not. If it wants to block, * we'll return to the scheduler and try again later. */ static void smtp_put_line_cb(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); smtp_checker_t *smtp_checker = CHECKER_ARG(checker); conn_opts_t *smtp_host = checker->co; ssize_t w; /* Handle read timeout */ if (thread->type == THREAD_WRITE_TIMEOUT) { smtp_final(thread, "Write timeout to server %s" , FMT_SMTP_RS(smtp_host)); return; } /* write the data */ w = write(thread->u.f.fd, smtp_checker->buff, smtp_checker->buff_ctr); if (w == -1 && (check_EAGAIN(errno) || check_EINTR(errno))) { thread_add_write(thread->master, smtp_put_line_cb, checker, thread->u.f.fd, smtp_host->connection_to, THREAD_DESTROY_CLOSE_FD); return; } #ifdef _CHECKER_DEBUG_ if (do_checker_debug) log_message(LOG_DEBUG, "SMTP_CHECK %s > %s" , FMT_SMTP_RS(smtp_host) , smtp_checker->buff); #endif /* * If the connection was closed or there was * some sort of error, notify smtp_final() */ if (w <= 0) { smtp_final(thread, "Write failure to server %s" , FMT_SMTP_RS(smtp_host)); return; } /* Execute the callback */ smtp_engine_thread(thread); } /* * This is the same as smtp_get_line() except that we're sending a * line of data instead of receiving one. */ static void smtp_put_line(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); smtp_checker_t *smtp_checker = CHECKER_ARG(checker); smtp_checker->buff_ctr = strlen(smtp_checker->buff); /* schedule the I/O with our helper function */ smtp_put_line_cb(thread); return; } /* * Ok, our goal here is to snag the status code out of the * buffer and return it as an integer. If it's not legible, * return -1. */ static int smtp_get_status(smtp_checker_t *smtp_checker) { char *buff = smtp_checker->buff; int status; char *endptr; status = strtoul(buff, &endptr, 10); if (endptr - buff != 3 || (*endptr && *endptr != ' ')) return -1; return status; } /* * We have a connected socket and are ready to begin * the conversation. This function schedules itself to * be called via callbacks and tracking state in * smtp_checker->state. Upon first calling, smtp_checker->state * should be set to SMTP_START. */ static void smtp_engine_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); smtp_checker_t *smtp_checker = CHECKER_ARG(checker); conn_opts_t *smtp_host = checker->co; switch (smtp_checker->state) { /* First step, schedule to receive the greeting banner */ case SMTP_START: /* * Ok, if smtp_get_line schedules us back, we will * have data to analyze. Otherwise, smtp_get_line * will defer directly to smtp_final. */ smtp_checker->state = SMTP_HAVE_BANNER; smtp_get_line(thread); break; /* Second step, analyze banner, send HELO */ case SMTP_HAVE_BANNER: /* Check for "220 some.mailserver.com" in the greeting */ if (smtp_get_status(smtp_checker) != 220) { smtp_final(thread, "Bad greeting banner from server %s" , FMT_SMTP_RS(smtp_host)); } else { /* * Schedule to send the HELO, smtp_put_line will * defer directly to smtp_final on error. */ smtp_checker->state = SMTP_SENT_HELO; snprintf(smtp_checker->buff, SMTP_BUFF_MAX, "HELO %s\r\n", smtp_checker->helo_name); smtp_put_line(thread); } break; /* Third step, schedule to read the HELO response */ case SMTP_SENT_HELO: smtp_checker->state = SMTP_RECV_HELO; smtp_get_line(thread); break; /* Fourth step, analyze HELO return, send QUIT */ case SMTP_RECV_HELO: /* Check for "250 Please to meet you..." */ if (smtp_get_status(smtp_checker) != 250) { smtp_final(thread, "Bad HELO response from server %s" , FMT_SMTP_RS(smtp_host)); } else { smtp_checker->state = SMTP_SENT_QUIT; snprintf(smtp_checker->buff, SMTP_BUFF_MAX, "QUIT\r\n"); smtp_put_line(thread); } break; /* Fifth step, schedule to receive QUIT confirmation */ case SMTP_SENT_QUIT: smtp_checker->state = SMTP_RECV_QUIT; smtp_get_line(thread); break; /* Sixth step, wrap up success to smtp_final */ case SMTP_RECV_QUIT: smtp_final(thread, NULL); break; default: /* We shouldn't be here */ smtp_final(thread, "Unknown smtp engine state encountered"); break; } } /* * Second step in the process. Here we'll see if the connection * to the host we're checking was successful or not. */ static void smtp_check_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); smtp_checker_t *smtp_checker = CHECKER_ARG(checker); conn_opts_t *smtp_host = checker->co; int status; status = tcp_socket_state(thread, smtp_check_thread, 0); switch (status) { case connect_error: smtp_final(thread, "Error connecting to server %s" , FMT_SMTP_RS(smtp_host)); break; case connect_timeout: smtp_final(thread, "Connection timeout to server %s" , FMT_SMTP_RS(smtp_host)); break; case connect_fail: smtp_final(thread, "Could not connect to server %s" , FMT_SMTP_RS(smtp_host)); break; case connect_success: #ifdef _CHECKER_DEBUG_ if (do_checker_debug) log_message(LOG_DEBUG, "SMTP_CHECK Remote SMTP server %s connected", FMT_SMTP_RS(smtp_host)); #endif /* Enter the engine at SMTP_START */ smtp_checker->state = SMTP_START; smtp_engine_thread(thread); break; default: /* we shouldn't be here */ smtp_final(thread, "Unknown connection error to server %s" , FMT_SMTP_RS(smtp_host)); break; } } /* * This is the main thread, where all the action starts. * When the check daemon comes up, it goes down each real * server's checkers_queue and launches a thread for each * checker that got registered. This is the callback/event * function for that initial thread. * * It should be noted that we ARE responsible for scheduling * ourselves to run again. It doesn't have to be right here, * but eventually has to happen. */ static void smtp_connect_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); conn_opts_t *smtp_host; enum connect_result status; int sd; /* Let's review our data structures. * * Thread is the structure used by the sceduler * for scheduling many types of events. thread->arg in this * case points to a checker structure. The checker * structure holds data about the vs and rs configurations * as well as the delay loop, etc. Each real server * defined in the keepalived.conf will more than likely have * a checker structure assigned to it. Each checker structure * has a data element that is meant to hold per checker * configurations. So thread->arg(checker)->data points to * a smtp_checker structure. In the smtp_checker structure * we hold global configuration data for the smtp check. * * So this whole thing looks like this: * thread->arg(checker)->data(smtp_checker)->host(smtp_host) * * To make life simple, we'll break the structures out so * that "checker" always points to the current checker structure, * "smtp_checker" points to the current smtp_checker structure. */ /* * If we're disabled, we'll do nothing at all. * But we still have to register ourselves again so * we don't fall of the face of the earth. */ if (!checker->enabled) { thread_add_timer(thread->master, smtp_start_check_thread, checker, checker->delay_loop); return; } smtp_host = checker->co; /* Create the socket, failing here should be an oddity */ if ((sd = socket(smtp_host->dst.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_TCP)) == -1) { log_message(LOG_INFO, "SMTP_CHECK connection failed to create socket. Rescheduling."); thread_add_timer(thread->master, smtp_start_check_thread, checker, checker->delay_loop); return; } status = tcp_bind_connect(sd, smtp_host); /* handle tcp connection status & register callback the next step in the process */ if (tcp_connection_state(sd, status, thread, smtp_check_thread, smtp_host->connection_to, 0)) { if (status == connect_fail) { close(sd); smtp_final(thread, "Network unreachable for server %s - real server %s", inet_sockaddrtos(&checker->co->dst), inet_sockaddrtopair(&checker->rs->addr)); } else { close(sd); log_message(LOG_INFO, "SMTP_CHECK socket bind failed. Rescheduling."); thread_add_timer(thread->master, smtp_start_check_thread, checker, checker->delay_loop); } } } static void smtp_start_check_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); checker->retry_it = 0; smtp_connect_thread(thread); } #ifdef THREAD_DUMP void register_check_smtp_addresses(void) { register_thread_address("smtp_start_check_thread", smtp_start_check_thread); register_thread_address("smtp_check_thread", smtp_check_thread); register_thread_address("smtp_connect_thread", smtp_connect_thread); register_thread_address("smtp_get_line_cb", smtp_get_line_cb); register_thread_address("smtp_put_line_cb", smtp_put_line_cb); } #endif keepalived-2.3.3/keepalived/check/check_http.c0000664000175000017500000014760614701304404014774 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: WEB CHECK. Common HTTP/SSL checker primitives. * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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, */ #include "config.h" #include #include #include #include #include #include #include #ifdef _WITH_REGEX_CHECK_ #define PCRE2_CODE_UNIT_WIDTH 8 #include #endif #include "check_api.h" #include "check_http.h" #include "check_ssl.h" #include "bitops.h" #include "logger.h" #include "parser.h" #include "utils.h" #include "html.h" #include "layer4.h" #include "ipwrapper.h" #include "smtp.h" #ifdef THREAD_DUMP #include "scheduler.h" #endif #include "check_parser.h" typedef enum { REGISTER_CHECKER_NEW, REGISTER_CHECKER_RETRY, REGISTER_CHECKER_FAILED } register_checker_t; #ifdef _WITH_REGEX_CHECK_ typedef struct { const char *option; unsigned option_bit ; } regex_option_t; regex_option_t regex_options[] = { {"allow_empty_class", PCRE2_ALLOW_EMPTY_CLASS}, {"alt_bsux", PCRE2_ALT_BSUX}, {"auto_callout", PCRE2_AUTO_CALLOUT}, {"caseless", PCRE2_CASELESS}, {"dollar_endonly", PCRE2_DOLLAR_ENDONLY}, {"dotall", PCRE2_DOTALL}, {"dupnames", PCRE2_DUPNAMES}, {"extended", PCRE2_EXTENDED}, {"firstline", PCRE2_FIRSTLINE}, {"match_unset_backref", PCRE2_MATCH_UNSET_BACKREF}, {"multiline", PCRE2_MULTILINE}, {"never_ucp", PCRE2_NEVER_UCP}, {"never_utf", PCRE2_NEVER_UTF}, {"no_auto_capture", PCRE2_NO_AUTO_CAPTURE}, {"no_auto_possess", PCRE2_NO_AUTO_POSSESS}, {"no_dotstar_anchor", PCRE2_NO_DOTSTAR_ANCHOR}, {"no_start_optimize", PCRE2_NO_START_OPTIMIZE}, {"ucp", PCRE2_UCP}, {"ungreedy", PCRE2_UNGREEDY}, {"utf", PCRE2_UTF}, {"never_backslash_c", PCRE2_NEVER_BACKSLASH_C}, {"alt_circumflex", PCRE2_ALT_CIRCUMFLEX}, {"alt_verbnames", PCRE2_ALT_VERBNAMES}, {"use_offset_limit", PCRE2_USE_OFFSET_LIMIT}, {NULL, 0} }; /* Used for holding regex details during configuration */ static const unsigned char *conf_regex_pattern; static int conf_regex_options; #ifndef PCRE2_DONT_USE_JIT static PCRE2_SIZE jit_stack_start; static PCRE2_SIZE jit_stack_max; static pcre2_match_context *mcontext; static pcre2_jit_stack *jit_stack; #endif static LIST_HEAD_INITIALIZE(regexs); /* regex_t */ #ifdef _WITH_REGEX_TIMERS_ struct timespec total_regex_times; unsigned total_num_matches; unsigned total_regex_urls; bool do_regex_timers; #endif #endif #ifdef _REGEX_DEBUG_ bool do_regex_debug; #endif static url_t *current_url; /* GET processing command */ static const char *request_template = "GET %s HTTP/1.%d\r\n" "User-Agent: KeepAliveClient\r\n" "%s" "Host: %s%s\r\n\r\n"; static const char *request_template_ipv6 = "GET %s HTTP/1.%d\r\n" "User-Agent: KeepAliveClient\r\n" "%s" "Host: [%s]%s\r\n\r\n"; /* Output delimiters */ #define DELIM_BEGIN "-----------------------[" #define DELIM_END "]-----------------------\n" #define HTTP_HEADER_HEXA DELIM_BEGIN" HTTP Header Buffer "DELIM_END #define HTTP_HEADER_ASCII DELIM_BEGIN" HTTP Header Ascii Buffer "DELIM_END #define HTML_HEADER_HEXA DELIM_BEGIN" HTML Buffer "DELIM_END #define HTML_HASH DELIM_BEGIN" HTML hash resulting "DELIM_END #define HTML_HASH_FINAL DELIM_BEGIN" HTML hash final resulting "DELIM_END #ifdef _WITH_REGEX_CHECK_ static void free_regex(regex_t *regex) { list_del_init(®ex->e_list); /* Free up the regular expression. */ FREE_CONST_PTR(regex->pattern); pcre2_code_free(regex->pcre2_reCompiled); pcre2_match_data_free(regex->pcre2_match_data); #ifdef _WITH_REGEX_TIMERS_ total_regex_times.tv_sec += regex->regex_time.tv_sec; total_regex_times.tv_nsec += regex->regex_time.tv_nsec; if (total_regex_times.tv_nsec >= 1000000000L) { total_regex_times.tv_sec += total_regex_times.tv_nsec / 1000000000L; total_regex_times.tv_nsec %= 1000000000L; } total_num_matches += regex->num_match_calls; total_regex_urls += regex->num_regex_urls; #endif FREE(regex); } #endif static void free_url(url_t *url) { list_del_init(&url->e_list); FREE_CONST_PTR(url->path); FREE_CONST_PTR(url->digest); FREE_CONST_PTR(url->virtualhost); #ifdef _WITH_REGEX_CHECK_ if (url->regex) { if (!--url->regex->refcnt) { free_regex(url->regex); if (list_empty(®exs)) { #ifndef PCRE2_DONT_USE_JIT if (mcontext) { pcre2_match_context_free(mcontext); mcontext = NULL; } if (jit_stack) { pcre2_jit_stack_free(jit_stack); jit_stack = NULL; } #endif #ifdef _WITH_REGEX_TIMERS_ if (do_regex_timers) log_message(LOG_INFO, "Total regex time %" PRI_ts_sec ".%9.9" PRI_ts_nsec ", num match calls %u, num url checks %u", total_regex_times.tv_sec, total_regex_times.tv_nsec, total_num_matches, total_regex_urls); #endif } } } #endif FREE(url); } static void free_url_list(list_head_t *l) { url_t *url, *url_tmp; list_for_each_entry_safe(url, url_tmp, l, e_list) free_url(url); } static char * format_digest(const uint8_t *digest, char *buf) { int i; for (i = 0; i < MD5_DIGEST_LENGTH; i++) snprintf(buf + 2 * i, 2 + 1, "%2.2x", digest[i]); return buf; } static void dump_url(FILE *fp, bool is_ssl, const url_t *url) { char digest_buf[2 * MD5_DIGEST_LENGTH + 1]; unsigned int i = 0; unsigned min = 0; conf_write(fp, " Checked url = %s", url->path); if (url->digest) conf_write(fp, " digest = %s", format_digest(url->digest, digest_buf)); if (is_ssl) conf_write(fp, " tls_compliant %sset", url->tls_compliant ? "" : "un"); conf_write(fp, " HTTP Status Code(s)"); for (i = HTTP_STATUS_CODE_MIN; i <= HTTP_STATUS_CODE_MAX; i++) { if (__test_bit_array(i - HTTP_STATUS_CODE_MIN, url->status_code)) { if (!min) min = i; } else { if (!min) continue; if (i - 1 == min) conf_write(fp, " %u", min); else conf_write(fp, " %u-%u", min, i - 1); min = 0; } } if (min == HTTP_STATUS_CODE_MAX) conf_write(fp, " %u", min); else if (min) conf_write(fp, " %u-%d", min, HTTP_STATUS_CODE_MAX); if (url->virtualhost) conf_write(fp, " Virtual host = %s", url->virtualhost); if (url->last_ssl_error) conf_write(fp, " Last SSL error = 0x%lx", url->last_ssl_error); #ifdef _WITH_REGEX_CHECK_ if (url->regex) { char options_buf[512] = ""; char *op; conf_write(fp, " Regex = \"%s\"", url->regex->pattern); if (url->regex_no_match) conf_write(fp, " Regex no match"); if (url->regex_min_offset || url->regex_max_offset) { if (url->regex_max_offset) conf_write(fp, " Regex min offset = %zu, max_offset = %zu", url->regex_min_offset, url->regex_max_offset - 1); else conf_write(fp, " Regex min offset = %zu", url->regex_min_offset); } if (url->regex->pcre2_options) { op = options_buf; for (i = 0; regex_options[i].option; i++) { if (regex_options[i].option_bit) { *op++ = ' '; strcpy(op, regex_options[i].option); op += strlen(op); } } } conf_write(fp, " Regex options:%s", options_buf); conf_write(fp, " Regex ref count = %u", url->regex->refcnt); #ifndef PCRE2_DONT_USE_JIT if (url->regex_use_stack) conf_write(fp, " Regex stack start %zu, max %zu", jit_stack_start, jit_stack_max); #endif } #endif } static void dump_url_list(FILE *fp, bool is_ssl, const list_head_t *l) { url_t *url; list_for_each_entry(url, l, e_list) dump_url(fp, is_ssl, url); } static void free_http_request(request_t *req) { if(!req) return; if (req->ssl) SSL_free(req->ssl); FREE_PTR(req->buffer); FREE(req); } void free_http_check(checker_t *checker) { http_checker_t *http_get_chk = checker->data; free_url_list(&http_get_chk->url); free_http_request(http_get_chk->req); FREE_CONST_PTR(http_get_chk->virtualhost); FREE_PTR(http_get_chk); FREE(checker->co); FREE(checker); } static void dump_http_check(FILE *fp, const checker_t *checker) { const http_checker_t *http_get_chk = checker->data; conf_write(fp, " Keepalive method = %s_GET, http protocol %s", http_get_chk->proto == PROTO_HTTP ? "HTTP" : "SSL", http_get_chk->http_protocol == HTTP_PROTOCOL_1_0C ? "1.0C" : http_get_chk->http_protocol == HTTP_PROTOCOL_1_1 ? "1.1" : "1.0"); if (http_get_chk->virtualhost) conf_write(fp, " Virtualhost = %s", http_get_chk->virtualhost); #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ conf_write(fp, " Enable SNI %sset", http_get_chk->enable_sni ? "" : "un"); #endif conf_write(fp, " Fast recovery %sset", http_get_chk->fast_recovery ? "" : "un"); if (http_get_chk->proto == PROTO_SSL) conf_write(fp, " tls_compliant %sset", http_get_chk->tls_compliant ? "" : "un"); dump_url_list(fp, http_get_chk->proto, &http_get_chk->url); if (http_get_chk->failed_url) conf_write(fp, " Failed URL = %s", http_get_chk->failed_url->path); } static http_checker_t * alloc_http_get(const char *proto) { http_checker_t *new; PMALLOC(new); INIT_LIST_HEAD(&new->url); new->proto = (!strcmp(proto, "HTTP_GET")) ? PROTO_HTTP : PROTO_SSL; new->http_protocol = HTTP_PROTOCOL_1_0; new->virtualhost = NULL; if (new->proto == PROTO_SSL) check_data->ssl_required = true; return new; } static size_t __attribute__ ((pure)) url_list_size(const list_head_t *l) { size_t cnt = 0; list_head_t *e; list_for_each(e, l) cnt++; return cnt; } static bool __attribute__((pure)) compare_http_check(const checker_t *old_c, checker_t *new_c) { const http_checker_t *old = old_c->data; const http_checker_t *new = new_c->data; url_t *u1, *u2 = NULL; unsigned i; if (!compare_conn_opts(old_c->co, new_c->co)) return false; if (url_list_size(&old->url) != url_list_size(&new->url)) return false; if (!old->virtualhost != !new->virtualhost) return false; if (old->virtualhost && strcmp(old->virtualhost, new->virtualhost)) return false; list_for_each_entry(u1, &old->url, e_list) { u2 = (!u2) ? list_first_entry(&new->url, url_t, e_list) : list_entry(u2->e_list.next, url_t, e_list); if (strcmp(u1->path, u2->path)) return false; if (!u1->digest != !u2->digest) return false; if (u1->digest && memcmp(u1->digest, u2->digest, MD5_DIGEST_LENGTH)) return false; for (i = 0; i < sizeof(u1->status_code) / sizeof(u1->status_code[0]); i++) { if (u1->status_code[i] != u2->status_code[i]) return false; } if (!u1->virtualhost != !u2->virtualhost) return false; if (u1->virtualhost && strcmp(u1->virtualhost, u2->virtualhost)) return false; #ifdef _WITH_REGEX_CHECK_ if (!u1->regex != !u2->regex) return false; if (u1->regex) { if (strcmp(PTR_CAST_CONST(char, u1->regex->pattern), PTR_CAST_CONST(char, u2->regex->pattern))) return false; if (u1->regex->pcre2_options != u2->regex->pcre2_options) return false; if (u1->regex_no_match != u2->regex_no_match) return false; if (u1->regex_min_offset != u2->regex_min_offset || u1->regex_max_offset != u2->regex_max_offset) return false; } #endif } return true; } static void migrate_http_check(checker_t *new_c, const checker_t *old_c) { url_t *old_url; url_t *new_url; http_checker_t *old_http_c; http_checker_t *new_http_c; if (new_c->is_up) return; old_http_c = CHECKER_ARG(old_c); new_http_c = CHECKER_ARG(new_c); /* For the real servers to be the same, the checkers must match, * which means that the urls match */ new_url = list_first_entry(&new_http_c->url, url_t, e_list); list_for_each_entry(old_url, &old_http_c->url, e_list) { if (old_url == old_http_c->failed_url) { new_http_c->failed_url = new_url; return; } new_url = list_entry(new_url->e_list.next, url_t, e_list); } } static const checker_funcs_t http_checker_funcs = { CHECKER_HTTP, free_http_check, dump_http_check, compare_http_check, migrate_http_check }; /* Configuration stream handling */ static void http_get_handler(const vector_t *strvec) { http_checker_t *http_get_chk; const char *str = strvec_slot(strvec, 0); /* queue new checker */ http_get_chk = alloc_http_get(str); queue_checker(&http_checker_funcs, http_connect_thread, http_get_chk, CHECKER_NEW_CO(), true); current_checker->default_delay_before_retry = 3 * TIMER_HZ; } static void http_get_retry_handler(const vector_t *strvec) { unsigned retry; report_config_error(CONFIG_GENERAL_ERROR, "nb_get_retry is deprecated - please use 'retry'"); if (!read_unsigned_strvec(strvec, 1, &retry, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid nb_get_retry value '%s'", strvec_slot(strvec, 1)); return; } current_checker->retry = retry; } static void virtualhost_handler(const vector_t *strvec) { http_checker_t *http_get_chk = current_checker->data; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "HTTP_GET virtualhost name missing"); return; } set_string(&http_get_chk->virtualhost, strvec, "virtualhost"); } static void http_get_check_end(void) { http_checker_t *http_get_chk = current_checker->data; if (list_empty(&http_get_chk->url)) { report_config_error(CONFIG_GENERAL_ERROR, "HTTP/SSL_GET checker has no urls specified - ignoring"); dequeue_new_checker(); return; } if (!check_conn_opts(current_checker->co)) { dequeue_new_checker(); return; } } static void url_handler(__attribute__((unused)) const vector_t *strvec) { /* allocate the new URL */ PMALLOC(current_url); INIT_LIST_HEAD(¤t_url->e_list); #ifdef _WITH_REGEX_CHECK_ conf_regex_options = 0; #endif } static void path_handler(const vector_t *strvec) { set_string(¤t_url->path, strvec, "path"); } static void digest_handler(const vector_t *strvec) { char *digest; char *endptr; int i; uint8_t *digest_buf; digest = STRDUP(strvec_slot(strvec, 1)); if (current_url->digest) { report_config_error(CONFIG_GENERAL_ERROR, "Digest '%s' is a duplicate", digest); FREE(digest); return; } if (strlen(digest) != 2 * MD5_DIGEST_LENGTH) { report_config_error(CONFIG_GENERAL_ERROR, "digest '%s' character length should be %d rather than %zu", digest, 2 * MD5_DIGEST_LENGTH, strlen(digest)); FREE(digest); return; } digest_buf = MALLOC(MD5_DIGEST_LENGTH); for (i = MD5_DIGEST_LENGTH - 1; i >= 0; i--) { digest[2 * i + 2] = '\0'; digest_buf[i] = strtoul(digest + 2 * i, &endptr, 16); if (endptr != digest + 2 * i + 2) { report_config_error(CONFIG_GENERAL_ERROR, "Unable to interpret hex digit in '%s' at offset %d/%d", digest, 2 * i, 2 * i + 1); FREE(digest_buf); FREE(digest); return; } } current_url->digest = digest_buf; FREE_CONST(digest); } static void status_code_handler(const vector_t *strvec) { const char *str; unsigned int i, j; char *endptr; unsigned min, max; for (i = 1; i < vector_size(strvec); i++) { str = vector_slot(strvec, i); min = strtoul(str, &endptr, 10); if (*endptr == '-') max = strtoul(endptr + 1, &endptr, 10); else max = min; if (*endptr || min < HTTP_STATUS_CODE_MIN || min > HTTP_STATUS_CODE_MAX || max < HTTP_STATUS_CODE_MIN || max > HTTP_STATUS_CODE_MAX || min > max) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid HTTP_GET status code '%s'", str); continue; } for (j = min; j <= max; j++) __set_bit_array(j - HTTP_STATUS_CODE_MIN, current_url->status_code); } } static void url_virtualhost_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "Missing HTTP_GET virtualhost name"); return; } set_string(¤t_url->virtualhost, strvec, "url virtualhost"); } static void url_tls_compliant_handler(const vector_t *strvec) { 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 tls_compliant option %s", strvec_slot(strvec, 1)); return; } } current_url->tls_compliant = res; } static void http_protocol_handler(const vector_t *strvec) { http_checker_t *http_get_chk = current_checker->data; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "Missing http_protocol version"); return; } if (!strcmp(strvec_slot(strvec, 1), "1.0")) http_get_chk->http_protocol = HTTP_PROTOCOL_1_0; else if (!strcmp(strvec_slot(strvec, 1), "1.1")) http_get_chk->http_protocol = HTTP_PROTOCOL_1_1; else if (!strcmp(strvec_slot(strvec, 1), "1.0C") || !strcmp(strvec_slot(strvec, 1), "1.0c")) http_get_chk->http_protocol = HTTP_PROTOCOL_1_0C; else { report_config_error(CONFIG_GENERAL_ERROR, "Invalid http_protocol version %s", strvec_slot(strvec, 1)); return; } } #ifdef _WITH_REGEX_CHECK_ static void regex_handler(__attribute__((unused)) const vector_t *strvec) { const vector_t *strvec_qe = alloc_strvec_quoted_escaped(NULL); if (vector_size(strvec_qe) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "regex missing or too many fields"); free_strvec(strvec_qe); return; } if (conf_regex_pattern) { report_config_error(CONFIG_GENERAL_ERROR, "Duplicate regex specified - replacing %s with %s", conf_regex_pattern, strvec_slot(strvec, 1)); FREE_CONST_PTR(conf_regex_pattern); } conf_regex_pattern = PTR_CAST_CONST(unsigned char, set_value(strvec_qe)); free_strvec(strvec_qe); } static void regex_no_match_handler(__attribute__((unused)) const vector_t *strvec) { current_url->regex_no_match = true; } static void regex_options_handler(const vector_t *strvec) { unsigned i, j; const char *str; for (i = 1; i < vector_size(strvec); i++) { str = strvec_slot(strvec, i); for (j = 0; regex_options[j].option; j++) { if (!strcmp(str, regex_options[j].option)) { conf_regex_options |= regex_options[j].option_bit; break; } } } } static size_t regex_offset_handler(const vector_t *strvec, const char *type) { char *endptr; unsigned long val; if (vector_size(strvec) != 2) { log_message(LOG_INFO, "Missing or too may options for regex_%s_offset", type); return 0; } val = strtoul(vector_slot(strvec, 1), &endptr, 10); if (*endptr) { log_message(LOG_INFO, "Invalid regex_%s_offset %s specified", type, strvec_slot(strvec, 1)); return 0; } return (size_t)val; } static void regex_min_offset_handler(const vector_t *strvec) { current_url->regex_min_offset = regex_offset_handler(strvec, "min"); } static void regex_max_offset_handler(const vector_t *strvec) { /* regex_max_offset is one beyond last acceptable position */ current_url->regex_max_offset = regex_offset_handler(strvec, "max") + 1; } #ifndef PCRE2_DONT_USE_JIT static void regex_stack_handler(const vector_t *strvec) { unsigned long stack_start, stack_max; char *endptr; if (vector_size(strvec) != 3) { log_message(LOG_INFO, "regex_stack requires start and max values"); return; } stack_start = strtoul(vector_slot(strvec, 1), &endptr, 10); if (*endptr) { log_message(LOG_INFO, "regex_stack invalid start value"); return; } stack_max = strtoul(vector_slot(strvec, 2), &endptr, 10); if (*endptr) { log_message(LOG_INFO, "regex_stack invalid max value"); return; } if (stack_start > stack_max) { log_message(LOG_INFO, "regex stack start cannot exceed max value"); return; } if (stack_start > jit_stack_start) jit_stack_start = stack_start; if (stack_max > jit_stack_max) jit_stack_max = stack_max; current_url->regex_use_stack = true; } #endif static void prepare_regex(url_t *url) { int pcreErrorNumber; PCRE2_SIZE pcreErrorOffset; PCRE2_UCHAR buffer[256]; regex_t *r; /* See if this regex has already been specified */ list_for_each_entry(r, ®exs, e_list) { if (r->pcre2_options == conf_regex_options && !strcmp(PTR_CAST_CONST(char, r->pattern), PTR_CAST_CONST(char, conf_regex_pattern))) { url->regex = r; FREE_CONST_PTR(conf_regex_pattern); url->regex->refcnt++; return; } } /* This is a new regex */ PMALLOC(r); INIT_LIST_HEAD(&r->e_list); r->pattern = conf_regex_pattern; r->pcre2_options = conf_regex_options; conf_regex_pattern = NULL; r->refcnt = 1; r->pcre2_reCompiled = pcre2_compile(r->pattern, PCRE2_ZERO_TERMINATED, r->pcre2_options, &pcreErrorNumber, &pcreErrorOffset, NULL); url->regex = r; /* pcre_compile returns NULL on error, and sets pcreErrorOffset & pcreErrorStr */ if(r->pcre2_reCompiled == NULL) { pcre2_get_error_message(pcreErrorNumber, buffer, sizeof buffer); log_message(LOG_INFO, "Invalid regex: '%s' at offset %zu: %s\n" , r->pattern, pcreErrorOffset, PTR_CAST(char, buffer)); FREE_CONST_PTR(r->pattern); FREE(r); return; } r->pcre2_match_data = pcre2_match_data_create_from_pattern(r->pcre2_reCompiled, NULL); pcre2_pattern_info(r->pcre2_reCompiled, PCRE2_INFO_MAXLOOKBEHIND, &r->pcre2_max_lookbehind); #ifndef PCRE2_DONT_USE_JIT if ((pcreErrorNumber = pcre2_jit_compile(r->pcre2_reCompiled, PCRE2_JIT_PARTIAL_HARD /* | PCRE2_JIT_COMPLETE */))) { pcre2_get_error_message(pcreErrorNumber, buffer, sizeof buffer); log_message(LOG_INFO, "Regex JIT compilation failed: '%s': %s\n" , r->pattern, PTR_CAST(char, buffer)); FREE_CONST_PTR(r->pattern); FREE(r); return; } #endif list_add_tail(&r->e_list, ®exs); } #endif #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ static void enable_sni_handler(const vector_t *strvec) { http_checker_t *http_get_chk = current_checker->data; 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 enable_sni parameter %s", strvec_slot(strvec, 1)); return; } } http_get_chk->enable_sni = res; } #endif static void fast_recovery_handler(const vector_t *strvec) { http_checker_t *http_get_chk = current_checker->data; 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 fast_recovery parameter %s", strvec_slot(strvec, 1)); return; } } http_get_chk->fast_recovery = res; } static void tls_compliant_handler(const vector_t *strvec) { http_checker_t *http_get_chk = current_checker->data; 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 tls_compliant option %s", strvec_slot(strvec, 1)); return; } } http_get_chk->tls_compliant = res; } static void url_check(void) { unsigned i; http_checker_t *http_get_chk = current_checker->data; if (!current_url->path) { report_config_error(CONFIG_GENERAL_ERROR, "HTTP/SSL_GET checker url has no path - ignoring"); free_url(current_url); return; } /* Set default status codes if none set */ for (i = 0; i < sizeof(current_url->status_code) / sizeof(current_url->status_code[0]); i++) { if (current_url->status_code[i]) break; } if (i >= sizeof(current_url->status_code) / sizeof(current_url->status_code[0])) { for (i = HTTP_DEFAULT_STATUS_CODE_MIN; i <= HTTP_DEFAULT_STATUS_CODE_MAX; i++) __set_bit_array(i - HTTP_STATUS_CODE_MIN, current_url->status_code); } #ifdef _WITH_REGEX_CHECK_ if (conf_regex_pattern) prepare_regex(current_url); else if (conf_regex_options || current_url->regex_no_match || current_url->regex_min_offset || current_url->regex_max_offset #ifndef PCRE2_DONT_USE_JIT || current_url->regex_use_stack #endif ) { log_message(LOG_INFO, "regex parameters specified without regex"); conf_regex_options = 0; current_url->regex_no_match = false; current_url->regex_min_offset = 0; current_url->regex_max_offset = 0; #ifndef PCRE2_DONT_USE_JIT current_url->regex_use_stack = false; #endif } if (current_url->regex_max_offset && current_url->regex_min_offset >= current_url->regex_max_offset) { log_message(LOG_INFO, "regex min offset %zu > regex_max_offset %zu - ignoring", current_url->regex_min_offset, current_url->regex_max_offset - 1); current_url->regex_min_offset = current_url->regex_max_offset = 0; } #endif list_add_tail(¤t_url->e_list, &http_get_chk->url); if (!http_get_chk->url_it) http_get_chk->url_it = current_url; } static void install_http_ssl_check_keyword(const char *keyword) { vpp_t check_ptr; vpp_t check_ptr1; install_keyword(keyword, &http_get_handler); check_ptr = install_sublevel(VPP ¤t_checker); install_checker_common_keywords(true); install_keyword("nb_get_retry", &http_get_retry_handler); /* Deprecated */ install_keyword("virtualhost", &virtualhost_handler); install_keyword("http_protocol", &http_protocol_handler); #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ install_keyword("enable_sni", &enable_sni_handler); #endif install_keyword("fast_recovery", &fast_recovery_handler); if (!strcmp(keyword, "SSL_GET")) install_keyword("tls_compliant", &tls_compliant_handler); install_keyword("url", &url_handler); check_ptr1 = install_sublevel(VPP ¤t_url); install_keyword("path", &path_handler); install_keyword("digest", &digest_handler); install_keyword("status_code", &status_code_handler); install_keyword("virtualhost", &url_virtualhost_handler); #ifdef _WITH_REGEX_CHECK_ install_keyword("regex", ®ex_handler); install_keyword("regex_no_match", ®ex_no_match_handler); install_keyword("regex_options", ®ex_options_handler); install_keyword("regex_min_offset", ®ex_min_offset_handler); install_keyword("regex_max_offset", ®ex_max_offset_handler); #ifndef PCRE2_DONT_USE_JIT install_keyword("regex_stack", ®ex_stack_handler); #endif #endif if (!strcmp(keyword, "SSL_GET")) install_keyword("tls_compliant", &url_tls_compliant_handler); install_level_end_handler(url_check); install_sublevel_end(check_ptr1); install_level_end_handler(http_get_check_end); install_sublevel_end(check_ptr); } void install_http_check_keyword(void) { install_http_ssl_check_keyword("HTTP_GET"); } void install_ssl_check_keyword(void) { install_http_ssl_check_keyword("SSL_GET"); } /* * The global design of this checker is the following : * * - All the actions are done asynchronously. * - All the actions handle timeout connection. * - All the actions handle error from low layer to upper * layers. * * The global synopsis of the inter-thread-call is : * * http_connect_thread (handle layer4 connect) * v * http_check_thread (handle SSL connect) * v * http_request (send SSL GET request) * v * http_response_thread (initialize read stream step) * / \ * / \ * v v * http_read_thread ssl_read_thread (perform HTTP|SSL stream) * v v * http_handle_response (next checker thread registration) */ /* * Simple epilog functions. Handling event timeout. * Finish the checker with memory management or url rety check. * * method == REGISTER_CHECKER_NEW => register a new checker thread * method == REGISTER_CHECKER_RETRY => register a retry on url checker thread * method == REGISTER_CHECKER_FAILED => register a checker on the failed URL */ static void epilog(thread_ref_t thread, register_checker_t method) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); request_t *req = http_get_check->req; unsigned long delay = 0; bool checker_was_up; bool rs_was_alive; if (method == REGISTER_CHECKER_NEW) { if (list_is_last(&http_get_check->url_it->e_list, &http_get_check->url)) http_get_check->url_it = NULL; else http_get_check->url_it = list_entry(http_get_check->url_it->e_list.next, url_t, e_list); checker->retry_it = 0; } else if (method == REGISTER_CHECKER_RETRY) checker->retry_it++; if (method == REGISTER_CHECKER_NEW && !http_get_check->url_it && !http_get_check->failed_url) { /* Check completed. All the url have been successfully checked. * check if server is currently alive. */ if (!checker->is_up || !checker->has_run) { log_message(LOG_INFO, "Remote Web server %s succeed on service." , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(UP, checker); if (!checker_was_up && checker->rs->smtp_alert && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> CHECK succeed on service <="); /* We have done all the checks, so mark as has run */ checker->has_run = true; } /* Reset it counters */ http_get_check->url_it = list_first_entry(&http_get_check->url, url_t, e_list); checker->retry_it = 0; } /* * The get retry implementation mean that we retry performing * a GET on the same url until the remote web server return * html buffer. This is sometime needed with some applications * servers. */ else if (method == REGISTER_CHECKER_RETRY && checker->retry_it > checker->retry) { if (checker->is_up || !checker->has_run) { if (checker->has_run && checker->retry) log_message(LOG_INFO , "%s_CHECK on service %s failed after %u retries." , (http_get_check->proto == PROTO_SSL) ? "SSL" : "HTTP" , FMT_CHK(checker) , checker->retry_it - 1); else log_message(LOG_INFO , "%s_CHECK on service %s failed." , (http_get_check->proto == PROTO_SSL) ? "SSL" : "HTTP" , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(DOWN, checker); if (checker_was_up && checker->rs->smtp_alert && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> CHECK failed on service" " : HTTP/SSL request failed <="); } /* Mark we have a failed URL */ http_get_check->failed_url = http_get_check->url_it; } else if (method == REGISTER_CHECKER_NEW && http_get_check->failed_url) { /* If the failed URL is now up, check all the URLs */ http_get_check->failed_url = NULL; http_get_check->url_it = list_first_entry(&http_get_check->url, url_t, e_list); checker->retry_it = 0; } /* register next timer thread */ if (method == REGISTER_CHECKER_NEW) { delay = checker->delay_loop; if (!checker->has_run) checker->retry_it = checker->retry; } else if (http_get_check->failed_url) delay = checker->delay_before_retry > checker->delay_loop ? checker->delay_before_retry : checker->delay_loop; else delay = checker->delay_before_retry; /* If req == NULL, fd is not created */ if (req) { free_http_request(req); http_get_check->req = NULL; thread_close_fd(thread); } /* Register next checker thread. * If the checker is not up, but we are not aware of any failure, * don't delay the checks if fast_recovery option specified. */ if (http_get_check->fast_recovery && (!checker->has_run || (!checker->is_up && !http_get_check->failed_url))) thread_add_event(thread->master, http_connect_thread, checker, 0); else thread_add_timer(thread->master, http_connect_thread, checker, delay); return; } void timeout_epilog(thread_ref_t thread, const char *debug_msg) { checker_t *checker = THREAD_ARG(thread); /* check if server is currently alive */ if (checker->is_up || !checker->has_run) { if (((http_checker_t *)checker->data)->genhash_flags & GENHASH) { printf("%s\n", debug_msg); thread_add_terminate_event(thread->master); return; } if (global_data->checker_log_all_failures || checker->log_all_failures) log_message(LOG_INFO, "%s server %s." , debug_msg , FMT_CHK(checker)); checker->has_run = true; epilog(thread, REGISTER_CHECKER_RETRY); return; } /* do not retry if server is already known as dead */ epilog(thread, REGISTER_CHECKER_FAILED); } /* return the url pointer of the current url iterator */ static inline url_t * fetch_next_url(http_checker_t * http_get_check) { return http_get_check->url_it; } #ifdef _WITH_REGEX_CHECK_ /* Returns true to indicate buffer must be preserved */ static bool check_regex(url_t *url, request_t *req) { PCRE2_SIZE *ovector; int pcreExecRet; size_t keep; size_t start_offset = 0; #ifdef _REGEX_DEBUG_ if (do_regex_debug) log_message(LOG_INFO, "matched %d, min_offset %zu max_offset %zu, subject_offset %zu req->len %zu lookbehind %u start_offset %zu" #ifdef _WITH_REGEX_TIMERS_ ", num_match_calls %u" #endif , req->regex_matched, url->regex_min_offset, url->regex_max_offset, req->regex_subject_offset, req->len, url->regex->pcre2_max_lookbehind, req->start_offset #ifdef _WITH_REGEX_TIMERS_ , req->num_match_calls #endif ); #endif /* If we have already matched the regex, there is no point in checking * any further */ if (req->regex_matched) return false; /* If the end of the current buffer doesn't reach the start offset specified, * then skip the check */ if (url->regex_min_offset) { if (req->regex_subject_offset + req->len < url->regex_min_offset - url->regex->pcre2_max_lookbehind) { req->regex_subject_offset += req->len; return false; } if (req->regex_subject_offset < url->regex_min_offset) start_offset = url->regex_min_offset - req->regex_subject_offset; } /* If we are beyond the end of where we want to check, then don't try matching */ if (url->regex_max_offset && req->regex_subject_offset + req->start_offset >= url->regex_max_offset) { req->regex_subject_offset += req->len; return false; } #ifndef PCRE2_DONT_USE_JIT if (url->regex_use_stack && !mcontext) { mcontext = pcre2_match_context_create(NULL); jit_stack = pcre2_jit_stack_create(jit_stack_start, jit_stack_max, NULL); pcre2_jit_stack_assign(mcontext, NULL, jit_stack); } #endif if (req->start_offset > start_offset) start_offset = req->start_offset; #ifdef _WITH_REGEX_TIMERS_ struct timespec time_before, time_after; clock_gettime(CLOCK_MONOTONIC_RAW, &time_before); #endif #ifndef PCRE2_DONT_USE_JIT pcreExecRet = pcre2_jit_match #else pcreExecRet = pcre2_match #endif (url->regex->pcre2_reCompiled, PTR_CAST(unsigned char, req->buffer), req->len, start_offset, PCRE2_PARTIAL_HARD, url->regex->pcre2_match_data, #ifndef PCRE2_DONT_USE_JIT url->regex_use_stack ? mcontext : NULL #else NULL #endif ); // context #ifdef _WITH_REGEX_TIMERS_ clock_gettime(CLOCK_MONOTONIC_RAW, &time_after); req->req_time.tv_sec += time_after.tv_sec - time_before.tv_sec; req->req_time.tv_nsec += time_after.tv_nsec - time_before.tv_nsec; if (req->req_time.tv_nsec >= 1000000000L) { req->req_time.tv_sec += req->req_time.tv_nsec / 1000000000L; req->req_time.tv_nsec %= 1000000000L; } req->num_match_calls++; #endif req->start_offset = 0; if (pcreExecRet == PCRE2_ERROR_PARTIAL) { ovector = pcre2_get_ovector_pointer(url->regex->pcre2_match_data); #ifdef _REGEX_DEBUG_ if (do_regex_debug) log_message(LOG_INFO, "Partial returned, ovector %zu, max_lookbehind %u", ovector[0], url->regex->pcre2_max_lookbehind); #endif if ((keep = ovector[0] - url->regex->pcre2_max_lookbehind) <= 0) keep = 0; if (keep) { req->start_offset = url->regex->pcre2_max_lookbehind; req->len -= keep; memmove(req->buffer, req->buffer + keep, req->len); req->regex_subject_offset += keep; } else if (req->len == MAX_BUFFER_LENGTH - 1) { req->regex_subject_offset += req->len; log_message(LOG_INFO, "Regex partial match preserve too large - discarding"); return false; } return true; } /* Report what happened in the pcre2_match call. */ if(pcreExecRet < 0) { req->regex_subject_offset += req->len; switch(pcreExecRet) { case PCRE2_ERROR_NOMATCH: /* This is not an error while doing partial matches */ #ifdef _REGEX_DEBUG_ if (do_regex_debug) log_message(LOG_INFO, "String did not match the regex pattern"); #endif break; case PCRE2_ERROR_NULL: log_message(LOG_INFO, "Something was null in regex match"); break; case PCRE2_ERROR_BADOPTION: log_message(LOG_INFO, "A bad option was passed to regex"); break; case PCRE2_ERROR_BADMAGIC: log_message(LOG_INFO, "Magic number bad (compiled regex corrupt?)"); break; case PCRE2_ERROR_NOMEMORY: log_message(LOG_INFO, "Regex an out of memory"); break; default: log_message(LOG_INFO, "Unknown regex error %d", pcreExecRet); break; } return false; } if(pcreExecRet == 0) log_message(LOG_INFO, "Too many substrings found"); ovector = pcre2_get_ovector_pointer(url->regex->pcre2_match_data); /* Check if there was a match at or before regex_max_offset */ if (!url->regex_max_offset || (req->regex_subject_offset + ovector[0] < url->regex_max_offset)) { req->regex_matched = true; #ifdef _REGEX_DEBUG_ if (do_regex_debug) log_message(LOG_INFO, "Result: We have a match at offset %zu - \"%.*s\"", req->regex_subject_offset + ovector[0], (int)(ovector[1] - ovector[0]), req->buffer + ovector[0]); } else { log_message(LOG_INFO, "Match found but %zu bytes beyond regex_max_offset(%zu)", req->regex_subject_offset + ovector[0] - (url->regex_max_offset - 1), url->regex_max_offset - 1); #endif } req->regex_subject_offset += req->len; return false; } #endif /* Handle response */ void http_handle_response(thread_ref_t thread, unsigned char digest[MD5_DIGEST_LENGTH], bool empty_buffer) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); request_t *req = http_get_check->req; url_t *url = fetch_next_url(http_get_check); const char *msg = "HTTP status code"; int r; /* Genhash mode ? */ if (http_get_check->genhash_flags) { if (empty_buffer) { fprintf(stderr, "no data received from remote webserver\n"); } else { for (r = 0; r < MD5_DIGEST_LENGTH; r++) printf("%02x", digest[r]); printf("\n"); } thread_add_terminate_event(thread->master); return; } /* First check if remote webserver returned data */ if (empty_buffer) { timeout_epilog(thread, "Read, no data received from "); return; } /* Next check the HTTP status code */ if (req->status_code < HTTP_STATUS_CODE_MIN || req->status_code > HTTP_STATUS_CODE_MAX || !__test_bit_array(req->status_code - HTTP_STATUS_CODE_MIN, url->status_code)) { timeout_epilog(thread, "HTTP status code error to"); return; } /* Report a length mismatch the first time we get the specific difference */ if (req->content_len != SIZE_MAX && req->content_len != req->rx_bytes) { if (url->len_mismatch != (ssize_t)req->content_len - (ssize_t)req->rx_bytes) { log_message(LOG_INFO, "http_check for RS %s VS %s url %s%s:" " content_length (%zu) does not match received bytes (%zu)" , FMT_RS(checker->rs, checker->vs) , FMT_VS(checker->vs) , url->virtualhost ? url->virtualhost : "" , url->path, req->content_len, req->rx_bytes); url->len_mismatch = (ssize_t)req->content_len - (ssize_t)req->rx_bytes; } } else url->len_mismatch = 0; /* Continue with MD5SUM */ if (url->digest) { /* Compute MD5SUM */ r = memcmp(url->digest, digest, MD5_DIGEST_LENGTH); if (r) { timeout_epilog(thread, "MD5 digest error to"); return; } msg = "MD5 digest"; } #ifdef _WITH_REGEX_CHECK_ /* Did a regex match? */ if (url->regex) { #ifdef _WITH_REGEX_TIMERS_ url->regex->regex_time.tv_sec += req->req_time.tv_sec; url->regex->regex_time.tv_nsec += req->req_time.tv_nsec; if (url->regex->regex_time.tv_nsec >= 1000000000L) { url->regex->regex_time.tv_sec += url->regex->regex_time.tv_nsec / 1000000000L; url->regex->regex_time.tv_nsec %= 1000000000L; } url->regex->num_match_calls += req->num_match_calls; url->regex->num_regex_urls++; #endif if (req->regex_matched == url->regex_no_match) { timeout_epilog(thread, "Regex match failed"); return; } msg = "Regex match"; } #endif if (!checker->is_up) { log_message(LOG_INFO, "%s success to %s url(%s)", msg , FMT_CHK(checker) , url->path); epilog(thread, REGISTER_CHECKER_NEW); return; } epilog(thread, REGISTER_CHECKER_NEW); } /* Dump HTTP header */ static void http_dump_header(char *buffer, size_t size) { dump_buffer(buffer, size, stdout, 0); printf(HTTP_HEADER_ASCII); printf("%.*s\n", (int)size, buffer); } void dump_digest(unsigned char *digest, unsigned len) { printf("\n"); printf(HTML_HASH); dump_buffer(PTR_CAST(char, digest), len, stdout, 0); printf(HTML_HASH_FINAL); } /* Handle response stream performing MD5 updates */ void http_process_response(thread_ref_t thread, request_t *req, size_t r, url_t *url) { size_t old_req_len = req->len; checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); req->len += r; req->buffer[req->len] = '\0'; /* Terminate the received data since it is used as a string */ if (!req->extracted) { if (http_get_check->genhash_flags & GENHASH_VERBOSE) printf(HTTP_HEADER_HEXA); if ((req->extracted = extract_html(req->buffer, req->len))) { req->status_code = extract_status_code(req->buffer, req->len); req->content_len = extract_content_length(req->buffer, req->len); if (http_get_check->genhash_flags & GENHASH_VERBOSE) http_dump_header(req->buffer, req->extracted - req->buffer); r = req->len - (size_t)(req->extracted - req->buffer); if (r && url->digest) { if (req->content_len == SIZE_MAX || req->content_len > req->rx_bytes) { EVP_DigestUpdate(req->context, req->extracted, req->content_len == SIZE_MAX || req->content_len >= req->rx_bytes + r ? r : req->content_len - req->rx_bytes); if (http_get_check->genhash_flags & GENHASH_VERBOSE) { printf(HTML_HEADER_HEXA); dump_buffer(req->extracted, req->content_len == SIZE_MAX || req->content_len >= req->rx_bytes + r ? r : req->content_len - req->rx_bytes, stdout, 0); } } } req->rx_bytes = r; #ifdef _WITH_REGEX_CHECK_ if (!r || !url->regex || !check_regex(url, req)) #endif req->len = 0; } } else if (req->len) { if (url->digest && (req->content_len == SIZE_MAX || req->content_len > req->rx_bytes)) { EVP_DigestUpdate(req->context, req->buffer + old_req_len, req->content_len == SIZE_MAX || req->content_len >= req->rx_bytes + r ? r : req->content_len - req->rx_bytes); if (http_get_check->genhash_flags & GENHASH_VERBOSE) dump_buffer(req->buffer + old_req_len, req->content_len == SIZE_MAX || req->content_len >= req->rx_bytes + r ? r : req->content_len - req->rx_bytes, stdout, 0); } req->rx_bytes += req->len; #ifdef _WITH_REGEX_CHECK_ if (!url->regex || !check_regex(url, req)) #endif req->len = 0; } } /* Asynchronous HTTP stream reader */ static void http_read_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); request_t *req = http_get_check->req; url_t *url = fetch_next_url(http_get_check); unsigned timeout = checker->co->connection_to; unsigned char digest[MD5_DIGEST_LENGTH]; ssize_t r = 0; /* Handle read timeout */ if (thread->type == THREAD_READ_TIMEOUT) { timeout_epilog(thread, "Timeout HTTP read"); return; } /* read the HTTP stream */ r = read(thread->u.f.fd, req->buffer + req->len, MAX_BUFFER_LENGTH - 1 - req->len); /* Allow space for adding '\0' */ /* Test if data are ready */ if (r == -1 && (check_EAGAIN(errno) || check_EINTR(errno))) { log_message(LOG_INFO, "Read error with server %s: %s" , FMT_CHK(checker) , strerror(errno)); thread_add_read(thread->master, http_read_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } if (r <= 0) { /* -1:error , 0:EOF */ /* All the HTTP stream has been parsed */ if (url->digest) { EVP_DigestFinal_ex(req->context, digest, NULL); EVP_MD_CTX_free(req->context); req->context = NULL; if (r == 0 && http_get_check->genhash_flags & GENHASH_VERBOSE) dump_digest(digest, MD5_DIGEST_LENGTH); } else digest[0] = 0; if (r == -1) { /* We have encountered a real read error */ timeout_epilog(thread, "Read error with"); return; } /* Handle response stream */ http_handle_response(thread, digest, !req->extracted); return; } /* Handle response stream */ http_process_response(thread, req, (size_t)r, url); /* * Register next http stream reader. * Register itself to not perturbe global I/O multiplexer. */ thread_add_read(thread->master, http_read_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); } /* * Read get result from the remote web server. * Apply trigger check to this result. */ static void http_response_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); request_t *req = http_get_check->req; url_t *url = fetch_next_url(http_get_check); unsigned timeout = checker->co->connection_to; /* Handle read timeout */ if (thread->type == THREAD_READ_TIMEOUT) { timeout_epilog(thread, "Timeout WEB read"); return; } /* Allocate & clean the get buffer */ req->buffer = PTR_CAST(char, MALLOC(MAX_BUFFER_LENGTH)); req->extracted = NULL; req->len = 0; req->error = 0; #ifdef _WITH_REGEX_CHECK_ req->regex_matched = false; req->regex_subject_offset = 0; #ifdef _WITH_REGEX_TIMERS_ req->num_match_calls = 0; #endif #endif if (url->digest) { req->context = EVP_MD_CTX_new(); EVP_DigestInit_ex(req->context, EVP_md5(), NULL); } /* Register asynchronous http/ssl read thread */ if (http_get_check->proto == PROTO_SSL) thread_add_read(thread->master, ssl_read_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); else thread_add_read(thread->master, http_read_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); } /* remote Web server is connected, send it the get url query. */ static void http_request(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); request_t *req = http_get_check->req; sockaddr_t *addr = &checker->co->dst; unsigned timeout = checker->co->connection_to; const char *vhost; const char *request_host; char request_host_port[7]; /* ":" [0-9][0-9][0-9][0-9][0-9] "\0" */ char *str_request; url_t *fetched_url; int ret = 0; /* Allocate & clean the GET string */ str_request = PTR_CAST(char, MALLOC(GET_BUFFER_LENGTH)); fetched_url = fetch_next_url(http_get_check); if (fetched_url->virtualhost) vhost = fetched_url->virtualhost; else if (http_get_check->virtualhost) vhost = http_get_check->virtualhost; else if (checker->rs->virtualhost) vhost = checker->rs->virtualhost; else if (checker->vs->virtualhost) vhost = checker->vs->virtualhost; else vhost = NULL; if (vhost) { /* If vhost was defined we don't need to override it's port */ request_host = vhost; request_host_port[0] = '\0'; } else { request_host = inet_sockaddrtos(addr); snprintf(request_host_port, sizeof(request_host_port), ":%d", ntohs(inet_sockaddrport(addr))); } /* if literal ipv6 address, use ipv6 template, see RFC 2732 */ snprintf(str_request, GET_BUFFER_LENGTH, (addr->ss_family == AF_INET6 && !vhost) ? request_template_ipv6 : request_template, fetched_url->path, http_get_check->http_protocol == HTTP_PROTOCOL_1_1 ? 1 : 0, http_get_check->http_protocol == HTTP_PROTOCOL_1_0C || http_get_check->http_protocol == HTTP_PROTOCOL_1_1 ? "Connection: close\r\n" : "", request_host, request_host_port); #ifdef _CHECKER_DEBUG_ if (do_checker_debug) log_message(LOG_DEBUG, "Processing url(%s) of %s.", fetched_url->path, FMT_CHK(checker)); #endif /* Send the GET request to remote Web server */ if (http_get_check->proto == PROTO_SSL) ret = ssl_send_request(req->ssl, str_request, (int)strlen(str_request)); else ret = (send(thread->u.f.fd, str_request, strlen(str_request), 0) != -1); FREE(str_request); if (!ret) { timeout_epilog(thread, "Cannot send get request to"); return; } /* Register read timeouted thread */ thread_add_read(thread->master, http_response_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); thread_del_write(thread); return; } /* WEB checkers threads */ static void http_check_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); int ret = 1; int status; unsigned long timeout = 0; int ssl_err = 0; bool new_req = false; status = tcp_socket_state(thread, http_check_thread, 0); if (status != connect_success) { if (status == connect_error) timeout_epilog(thread, "Error connecting"); else if (status == connect_timeout) timeout_epilog(thread, "Timeout connecting"); else if (status == connect_fail) timeout_epilog(thread, "Connection failed"); return; } if (!http_get_check->req) { PMALLOC(http_get_check->req); new_req = true; } else new_req = false; if (http_get_check->proto == PROTO_SSL) { if (thread->type == THREAD_WRITE_TIMEOUT || thread->type == THREAD_READ_TIMEOUT) { timeout_epilog(thread, "Timeout connecting"); return; } ret = ssl_connect(thread, new_req); if (ret < 0) { timeout = timer_long(thread->sands) - timer_long(time_now); ssl_err = SSL_get_error(http_get_check->req->ssl, ret); if (ssl_err == SSL_ERROR_WANT_READ) { thread_add_read(thread->master, http_check_thread, THREAD_ARG(thread), thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); thread_del_write(thread); return; } if (ssl_err == SSL_ERROR_WANT_WRITE) { thread_add_write(thread->master, http_check_thread, THREAD_ARG(thread), thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); thread_del_read(thread); return; } ret = 0; } else if (ret != 1) ret = 0; } if (ret) { /* Remote WEB server is connected. */ #ifdef _CHECKER_DEBUG_ if (do_checker_debug) log_message(LOG_DEBUG, "Remote Web server %s connected.", FMT_CHK(checker)); #endif http_request(thread); } else { #ifdef _CHECKER_DEBUG_ if (do_checker_debug) log_message(LOG_DEBUG, "Connection trouble to: %s." , FMT_CHK(checker)); if (http_get_check->proto == PROTO_SSL) ssl_printerr(SSL_get_error(http_get_check->req->ssl, ret)); #endif timeout_epilog(thread, "SSL handshake/communication error" " connecting to"); return; } } void http_connect_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); conn_opts_t *co = checker->co; url_t *fetched_url; enum connect_result status; int fd; /* * Register a new checker thread & return * if checker is disabled */ if (!checker->enabled) { thread_add_timer(thread->master, http_connect_thread, checker, checker->delay_loop); return; } /* if there are no URLs in list, enable server w/o checking */ fetched_url = fetch_next_url(http_get_check); if (!fetched_url) { epilog(thread, REGISTER_CHECKER_NEW); return; } /* Create the socket */ if ((fd = socket(co->dst.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_TCP)) == -1) { log_message(LOG_INFO, "WEB connection fail to create socket. Rescheduling."); thread_add_timer(thread->master, http_connect_thread, checker, checker->delay_loop); return; } status = tcp_bind_connect(fd, co); /* handle tcp connection status & register check worker thread */ if (tcp_connection_state(fd, status, thread, http_check_thread, co->connection_to, 0)) { close(fd); if (status == connect_fail) { timeout_epilog(thread, "HTTP/SSL_CHECK - network unreachable"); } else { log_message(LOG_INFO, "WEB socket bind failed. Rescheduling"); thread_add_timer(thread->master, http_connect_thread, checker, checker->delay_loop); } } } #ifdef THREAD_DUMP void register_check_http_addresses(void) { register_thread_address("http_check_thread", http_check_thread); register_thread_address("http_connect_thread", http_connect_thread); register_thread_address("http_read_thread", http_read_thread); register_thread_address("http_response_thread", http_response_thread); } #endif keepalived-2.3.3/keepalived/check/libipvs.c0000664000175000017500000010641014770004747014331 /* * libipvs: Library for manipulating IPVS through netlink or [gs]etsockopt * * This code is copied from the ipvsadm sources, with the unused * code removed. It is available at: * https://git.kernel.org/cgit/utils/kernel/ipvsadm/ipvsadm.git * * The upstream code should periodically be checked for updates, * which should then be applied to this code. * * Authors: Wensong Zhang * * 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. */ #include "config.h" #include #include #include #include #include #include #include #ifdef LIBIPVS_USE_NL #include #include #include //#define LIBNL_DEBUG #ifdef _HAVE_LIBNL1_ #ifndef _LIBNL_DYNAMIC_ #define nl_socket_alloc nl_handle_alloc #define nl_socket_free nl_handle_destroy #endif #endif #ifdef _LIBNL_DYNAMIC_ #include "libnl_link.h" #endif #endif #include "libipvs.h" #include "memory.h" #include "logger.h" #include "utils.h" #include "namespaces.h" #include "global_data.h" static int sockfd = -1; static void* ipvs_func = NULL; static ipvs_timeout_t orig_ipvs_timeouts; #ifdef LIBIPVS_USE_NL static struct nl_sock *sock = NULL; static nl_recvmsg_msg_cb_t cur_nl_sock_cb_func; static int family; static bool try_nl = true; static int nl_ack_flag; /* Policy definitions */ static struct nla_policy ipvs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = { [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED }, [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED }, [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED }, [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 }, [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 }, [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 }, }; #ifdef _WITH_SNMP_CHECKER_ static struct nla_policy ipvs_service_policy[IPVS_SVC_ATTR_MAX + 1] = { [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 }, [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 }, [IPVS_SVC_ATTR_ADDR] = { .type = NLA_UNSPEC, .maxlen = sizeof(struct in6_addr) }, [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 }, [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 }, [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_STRING, .maxlen = IP_VS_SCHEDNAME_MAXLEN - 1 }, [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_UNSPEC, .minlen = sizeof(struct ip_vs_flags), .maxlen = sizeof(struct ip_vs_flags) }, [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 }, [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 }, [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED }, [IPVS_SVC_ATTR_PE_NAME] = { .type = NLA_STRING, .maxlen = IP_VS_PENAME_MAXLEN }, #ifdef _WITH_LVS_64BIT_STATS_ [IPVS_SVC_ATTR_STATS64] = { .type = NLA_NESTED }, #endif }; static struct nla_policy ipvs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = { [IPVS_DEST_ATTR_ADDR] = { .type = NLA_UNSPEC, .maxlen = sizeof(struct in6_addr) }, [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 }, [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 }, [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 }, [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 }, [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 }, [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 }, [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 }, [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 }, [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED }, #if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY [IPVS_DEST_ATTR_ADDR_FAMILY] = { .type = NLA_U16 }, #endif #ifdef _HAVE_IPVS_TUN_TYPE_ [IPVS_DEST_ATTR_TUN_TYPE] = { .type = NLA_U8 }, [IPVS_DEST_ATTR_TUN_PORT] = { .type = NLA_U16 }, #endif #ifdef _HAVE_IPVS_TUN_CSUM_ [IPVS_DEST_ATTR_TUN_FLAGS] = { .type = NLA_U16 }, #endif #ifdef _WITH_LVS_64BIT_STATS_ [IPVS_DEST_ATTR_STATS64] = {.type = NLA_NESTED }, #endif }; #ifdef _WITH_LVS_64BIT_STATS_ static struct nla_policy ipvs_stats64_policy[IPVS_STATS_ATTR_MAX + 1] = { [IPVS_STATS_ATTR_CONNS] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_INPKTS] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_OUTPKTS] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_INBYTES] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_OUTBYTES] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_CPS] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_INPPS] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_OUTPPS] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_INBPS] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_OUTBPS] = { .type = NLA_U64 }, }; #endif static struct nla_policy ipvs_stats_policy[IPVS_STATS_ATTR_MAX + 1] = { [IPVS_STATS_ATTR_CONNS] = { .type = NLA_U32 }, [IPVS_STATS_ATTR_INPKTS] = { .type = NLA_U32 }, [IPVS_STATS_ATTR_OUTPKTS] = { .type = NLA_U32 }, [IPVS_STATS_ATTR_INBYTES] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_OUTBYTES] = { .type = NLA_U64 }, [IPVS_STATS_ATTR_CPS] = { .type = NLA_U32 }, [IPVS_STATS_ATTR_INPPS] = { .type = NLA_U32 }, [IPVS_STATS_ATTR_OUTPPS] = { .type = NLA_U32 }, [IPVS_STATS_ATTR_INBPS] = { .type = NLA_U32 }, [IPVS_STATS_ATTR_OUTBPS] = { .type = NLA_U32 }, }; #endif /* _WITH_SNMP_CHECKER_ */ static struct nla_policy ipvs_info_policy[IPVS_INFO_ATTR_MAX + 1] = { [IPVS_INFO_ATTR_VERSION] = { .type = NLA_U32 }, [IPVS_INFO_ATTR_CONN_TAB_SIZE] = { .type = NLA_U32 }, }; #endif #ifdef LIBIPVS_USE_NL #ifndef NLA_PUT_S32 #define NLA_PUT_S32(msg, attrtype, value) \ NLA_PUT_TYPE(msg, int32_t, attrtype, value) static inline int32_t nla_get_s32(struct nlattr *attr) { return (int32_t)nla_get_u32(attr); } #endif #ifndef _HAVE_LIBNL1_ static int nlerr2syserr(int err) { switch (abs(err)) { case NLE_BAD_SOCK: return EBADF; case NLE_EXIST: return EEXIST; case NLE_NOADDR: return EADDRNOTAVAIL; case NLE_OBJ_NOTFOUND: return ENOENT; case NLE_INTR: return EINTR; case NLE_AGAIN: return EAGAIN; case NLE_INVAL: return EINVAL; case NLE_NOACCESS: return EACCES; case NLE_NOMEM: return ENOMEM; case NLE_AF_NOSUPPORT: return EAFNOSUPPORT; case NLE_PROTO_MISMATCH: return EPROTONOSUPPORT; case NLE_OPNOTSUPP: return EOPNOTSUPP; case NLE_PERM: return EPERM; case NLE_BUSY: return EBUSY; case NLE_RANGE: return ERANGE; case NLE_NODEV: return ENODEV; default: return err; } } #endif static struct nl_msg *ipvs_nl_message(uint8_t cmd, int flags) { struct nl_msg *msg; msg = nlmsg_alloc(); if (!msg) return NULL; genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, flags, cmd, IPVS_GENL_VERSION); return msg; } #ifdef LIBNL_DEBUG static void dump_nl_msg(const char *msg, struct nl_msg *nlmsg) { FILE *fp; const char *filename; filename = make_tmp_filename("nlmsg.dmp"); fp = fopen(filename, "a"); FREE_CONST(filename); fprintf(fp, "\n%s\n\n", msg); if (nlmsg) nl_msg_dump(nlmsg, fp); fclose(fp); } #endif static int ipvs_nl_noop_cb(__attribute__((unused)) struct nl_msg *msg, __attribute__((unused)) void *arg) { #ifdef LIBNL_DEBUG dump_nl_msg("Noop CB", msg); #endif return NL_OK; } #ifdef LIBNL_DEBUG static int recv_cb(struct nl_msg *msg, __attribute__((unused)) void *arg) { dump_nl_msg("Receive message", msg); return NL_OK; } #endif static int recv_ack_cb(__attribute__((unused)) struct nl_msg *msg, void *arg) { int *ack_flag = arg; #ifdef LIBNL_DEBUG dump_nl_msg("That was an ACK message", NULL); #endif *ack_flag = 1; return NL_STOP; } static int finish_cb(__attribute__((unused)) struct nl_msg *msg, void *arg) { int *ack_flag = arg; #ifdef LIBNL_DEBUG dump_nl_msg("That was a multi done message", NULL); #endif *ack_flag = 1; return NL_STOP; } static int ipvs_nl_err_cb(__attribute__((unused)) struct sockaddr_nl *nla, __attribute__((unused)) struct nlmsgerr *nlerr, void *arg) { int *ack_flag = arg; #ifdef LIBNL_DEBUG dump_nl_msg("That was an ERROR message", NULL); #endif *ack_flag = 1; return NL_STOP; } static int open_nl_sock(void) { if (!(sock = nl_socket_alloc())) return -1; if ( nl_ipvs_connect(global_data->network_namespace_ipvs, sock) < 0 || (family = genl_ctrl_resolve(sock, IPVS_GENL_NAME)) < 0) { nl_socket_free(sock); sock = NULL; return -1; } /* We finish receiving if we get an error, an ACK, or a DONE for a multipart message */ #ifndef _HAVE_LIBNL1_ if (nl_socket_modify_err_cb(sock, NL_CB_CUSTOM, ipvs_nl_err_cb, &nl_ack_flag)) #else if (nl_cb_err(nl_socket_get_cb(sock), NL_CB_CUSTOM, ipvs_nl_err_cb, &nl_ack_flag)) #endif log_message(LOG_INFO, "Setting err_cb failed"); nl_socket_modify_cb(sock, NL_CB_ACK, NL_CB_CUSTOM, recv_ack_cb, &nl_ack_flag); nl_socket_modify_cb(sock, NL_CB_FINISH, NL_CB_CUSTOM, finish_cb, &nl_ack_flag); #ifdef LIBNL_DEBUG nl_socket_modify_cb(sock, NL_CB_MSG_IN, NL_CB_CUSTOM, recv_cb, 0); #endif return 0; } static int ipvs_nl_send_message(struct nl_msg *msg, nl_recvmsg_msg_cb_t func, void *arg) { int err = EINVAL; int ret = 0; if (!msg) return 0; if (!sock && open_nl_sock()) { nlmsg_free(msg); return -1; } if (func != cur_nl_sock_cb_func) { if (!nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, func, arg)) cur_nl_sock_cb_func = func; else log_message(LOG_INFO, "Setting libnl callback function failed"); } #ifdef LIBNL_DEBUG dump_nl_msg("Sending message", msg); #endif #ifndef _HAVE_LIBNL1_ if (nl_send_auto(sock, msg) >= 0) { #else if (nl_send_auto_complete(sock, msg) >= 0) { #endif nl_ack_flag = 0; do { if ((err = -nl_recvmsgs_default(sock)) > 0) { #ifdef _HAVE_LIBNL1_ errno = err; #else errno = nlerr2syserr(err); #endif ret = -1; } } while (!nl_ack_flag); } nlmsg_free(msg); return ret; } #endif #ifdef LIBIPVS_USE_NL static int ipvs_getinfo_parse_cb(struct nl_msg *msg, __attribute__((unused)) void *arg) { struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[IPVS_INFO_ATTR_MAX + 1]; if (genlmsg_parse(nlh, 0, attrs, IPVS_INFO_ATTR_MAX, ipvs_info_policy) != 0) return NL_STOP; if (!(attrs[IPVS_INFO_ATTR_VERSION] && attrs[IPVS_INFO_ATTR_CONN_TAB_SIZE])) return NL_STOP; return NL_OK; } #endif static int ipvs_getinfo( #ifndef LIBIPVS_USE_NL __attribute__((unused)) #endif bool retry) { socklen_t len; struct ip_vs_getinfo ipvs_info; #ifdef LIBIPVS_USE_NL unsigned retries = retry ? 1 : 0; #endif /* It appears that if we need to install the ip_vs module * (via keepalived_modprobe), then the first ipvs_nl_send_message() * call afterwards fails, but it succeeds thereafter. * * On some distros the call genl_ctrl_resolve(sock, IPVS_GENL_NAME) loads * the ip_vs module, so we don't need to call keepalived_modprobe(), and * we don't need to retry the ipvs_nl_send_message(). On the other hand, * if genl_ctrl_resolve() does not load the ip_vs module, then we need to * make the second call of ipvs_nl_send_message(). * * It appears that if there is an entry: * alias net-pf-16-proto-16-family-IPVS ip_vs * in /lib/modules/{KERNEL_VER}/modules.alias then genl_ctrl_resolve() causes * the ip_vs module to be loaded, and if there is no such entry, then it does * not load the ip_vs module. Whether this is cause and effect, or whether they * are both caused by some other aspect of the configuration I do not know. */ ipvs_func = ipvs_getinfo; #ifdef LIBIPVS_USE_NL if (try_nl) { struct nl_msg *msg; do { if (!(msg = ipvs_nl_message(IPVS_CMD_GET_INFO, 0))) return -1; if (!ipvs_nl_send_message(msg, ipvs_getinfo_parse_cb, NULL)) return 0; } while (retries--); return -1; } #endif len = sizeof(ipvs_info); return getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO, &ipvs_info, &len); } int ipvs_init( #ifndef LIBIPVS_USE_NL __attribute__((unused)) #endif bool retry) { ipvs_func = ipvs_init; #ifdef LIBIPVS_USE_NL #ifdef _LIBNL_DYNAMIC_ try_nl = libnl_init(); if (!try_nl) log_message(LOG_INFO, "Note: IPVS with IPv6 will not be supported"); #endif if (try_nl) return ipvs_getinfo(retry); try_nl = false; #else log_message(LOG_INFO, "Note: IPVS with IPv6 will not be supported"); #endif sockfd = socket_netns_name(global_data->network_namespace_ipvs, AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW); if (sockfd == -1) return -1; if (ipvs_getinfo(false)) { close(sockfd); sockfd = -1; return -1; } return 0; } int ipvs_flush(void) { #ifdef LIBIPVS_USE_NL if (try_nl) { struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_FLUSH, 0); if (msg && (ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL) == 0)) return 0; return -1; } #endif return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_FLUSH, NULL, 0); } #ifdef LIBIPVS_USE_NL static int ipvs_nl_fill_service_attr(struct nl_msg *msg, ipvs_service_t *svc) { struct nlattr *nl_service; struct ip_vs_flags flags = { .flags = svc->user.flags, .mask = ~0U }; nl_service = nla_nest_start(msg, IPVS_CMD_ATTR_SERVICE); if (!nl_service) return -1; NLA_PUT_U16(msg, IPVS_SVC_ATTR_AF, svc->af); if (svc->user.fwmark) { NLA_PUT_U32(msg, IPVS_SVC_ATTR_FWMARK, svc->user.fwmark); } else { NLA_PUT_U16(msg, IPVS_SVC_ATTR_PROTOCOL, svc->user.protocol); NLA_PUT(msg, IPVS_SVC_ATTR_ADDR, sizeof(svc->nf_addr), &(svc->nf_addr)); NLA_PUT_U16(msg, IPVS_SVC_ATTR_PORT, svc->user.port); } NLA_PUT_STRING(msg, IPVS_SVC_ATTR_SCHED_NAME, svc->user.sched_name); if (svc->pe_name[0]) NLA_PUT_STRING(msg, IPVS_SVC_ATTR_PE_NAME, svc->pe_name); NLA_PUT(msg, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags); NLA_PUT_U32(msg, IPVS_SVC_ATTR_TIMEOUT, svc->user.timeout); NLA_PUT_U32(msg, IPVS_SVC_ATTR_NETMASK, svc->user.netmask); nla_nest_end(msg, nl_service); return 0; nla_put_failure: return -1; } #endif static int ipvs_do_service(ipvs_service_t *svc, uint8_t cmd) { #ifdef LIBIPVS_USE_NL if (try_nl) { struct nl_msg *msg = ipvs_nl_message(cmd, 0); if (!msg) return -1; if (ipvs_nl_fill_service_attr(msg, svc)) { nlmsg_free(msg); return -1; } return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL); } #endif if (svc->af != AF_INET) { errno = EAFNOSUPPORT; return -1; } svc->user.addr = svc->nf_addr.ip; return setsockopt(sockfd, IPPROTO_IP, cmd == IPVS_CMD_NEW_SERVICE ? IP_VS_SO_SET_ADD : #ifdef _INCLUDE_UNUSED_CODE_ cmd == IPVS_CMD_ZERO ? IP_VS_SO_SET_ZERO : #endif cmd == IPVS_CMD_DEL_SERVICE ? IP_VS_SO_SET_DEL : IP_VS_SO_SET_EDIT, &svc->user, sizeof(svc->user)); } int ipvs_add_service(ipvs_service_t *svc) { ipvs_func = ipvs_add_service; return ipvs_do_service(svc, IPVS_CMD_NEW_SERVICE); } int ipvs_update_service(ipvs_service_t *svc) { ipvs_func = ipvs_update_service; return ipvs_do_service(svc, IPVS_CMD_SET_SERVICE); } int ipvs_del_service(ipvs_service_t *svc) { ipvs_func = ipvs_del_service; return ipvs_do_service(svc, IPVS_CMD_DEL_SERVICE); } #ifdef _INCLUDE_UNUSED_CODE_ int ipvs_zero_service(ipvs_service_t *svc) { ipvs_func = ipvs_zero_service; return ipvs_do_service(svc, IPVS_CMD_ZERO); } #endif #ifdef LIBIPVS_USE_NL static int ipvs_nl_fill_dest_attr(struct nl_msg *msg, ipvs_dest_t *dst) { struct nlattr *nl_dest; nl_dest = nla_nest_start(msg, IPVS_CMD_ATTR_DEST); if (!nl_dest) return -1; #if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY NLA_PUT_U16(msg, IPVS_DEST_ATTR_ADDR_FAMILY, dst->af); #endif NLA_PUT(msg, IPVS_DEST_ATTR_ADDR, sizeof(dst->nf_addr), &(dst->nf_addr)); NLA_PUT_U16(msg, IPVS_DEST_ATTR_PORT, dst->user.port); NLA_PUT_U32(msg, IPVS_DEST_ATTR_FWD_METHOD, dst->user.conn_flags & IP_VS_CONN_F_FWD_MASK); NLA_PUT_U32(msg, IPVS_DEST_ATTR_WEIGHT, (uint32_t)dst->user.weight); #ifdef _HAVE_IPVS_TUN_TYPE_ if ((dst->user.conn_flags & IP_VS_CONN_F_FWD_MASK) == IP_VS_CONN_F_TUNNEL) { NLA_PUT_U8 (msg, IPVS_DEST_ATTR_TUN_TYPE, dst->tun_type); if (dst->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) NLA_PUT_U16(msg, IPVS_DEST_ATTR_TUN_PORT, dst->tun_port); #ifdef _HAVE_IPVS_TUN_CSUM_ if (dst->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP) NLA_PUT_U16(msg, IPVS_DEST_ATTR_TUN_FLAGS, dst->tun_flags); #endif } #endif NLA_PUT_U32(msg, IPVS_DEST_ATTR_U_THRESH, dst->user.u_threshold); NLA_PUT_U32(msg, IPVS_DEST_ATTR_L_THRESH, dst->user.l_threshold); nla_nest_end(msg, nl_dest); return 0; nla_put_failure: return -1; } #endif static int ipvs_do_dest(ipvs_service_t *svc, ipvs_dest_t *dest, uint8_t cmd) { struct { struct ip_vs_service_user svc; struct ip_vs_dest_user dest; } svcdest; #ifdef LIBIPVS_USE_NL if (try_nl) { struct nl_msg *msg = ipvs_nl_message(cmd, 0); if (!msg) return -1; if (ipvs_nl_fill_service_attr(msg, svc)) goto nla_put_failure; if (ipvs_nl_fill_dest_attr(msg, dest)) goto nla_put_failure; return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL); nla_put_failure: nlmsg_free(msg); return -1; } #endif if (svc->af != AF_INET || dest->af != AF_INET) { errno = EAFNOSUPPORT; return -1; } svcdest.svc = svc->user; svcdest.dest = dest->user; svcdest.svc.addr = svc->nf_addr.ip; svcdest.dest.addr = dest->nf_addr.ip; return setsockopt(sockfd, IPPROTO_IP, cmd == IPVS_CMD_NEW_DEST ? IP_VS_SO_SET_ADDDEST : cmd == IPVS_CMD_SET_DEST ? IP_VS_SO_SET_EDITDEST : IP_VS_SO_SET_DELDEST, &svcdest, sizeof(svcdest)); } int ipvs_add_dest(ipvs_service_t *svc, ipvs_dest_t *dest) { ipvs_func = ipvs_add_dest; return ipvs_do_dest(svc, dest, IPVS_CMD_NEW_DEST); } int ipvs_update_dest(ipvs_service_t *svc, ipvs_dest_t *dest) { ipvs_func = ipvs_update_dest; return ipvs_do_dest(svc, dest, IPVS_CMD_SET_DEST); } int ipvs_del_dest(ipvs_service_t *svc, ipvs_dest_t *dest) { ipvs_func = ipvs_del_dest; return ipvs_do_dest(svc, dest, IPVS_CMD_DEL_DEST); } #ifdef LIBIPVS_USE_NL static int ipvs_timeout_parse_cb(struct nl_msg *msg, void *arg) { struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1]; ipvs_timeout_t *u = PTR_CAST(ipvs_timeout_t, arg); if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0) return -1; if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]) u->tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]); if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]) u->tcp_fin_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]); if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]) u->udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]); return NL_OK; } #endif static void ipvs_get_timeout(void) { socklen_t len; ipvs_func = ipvs_get_timeout; #ifdef LIBIPVS_USE_NL if (try_nl) { struct nl_msg *msg; msg = ipvs_nl_message(IPVS_CMD_GET_CONFIG, 0); if (!msg || ipvs_nl_send_message(msg, ipvs_timeout_parse_cb, &orig_ipvs_timeouts)) log_message(LOG_INFO, "Failed to get IPVS timeouts"); return; } #endif len = sizeof(orig_ipvs_timeouts); if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_TIMEOUT, &orig_ipvs_timeouts, &len)) { log_message(LOG_INFO, "Failed to get IPVS timeouts"); return; } } int ipvs_set_timeout(const ipvs_timeout_t *to) { ipvs_func = ipvs_set_timeout; /* If we are altering the timeouts, ensure we can restore the original values */ if (!orig_ipvs_timeouts.tcp_timeout) { if (!to) return 0; ipvs_get_timeout(); } #ifdef LIBIPVS_USE_NL if (try_nl) { struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_SET_CONFIG, 0); if (!msg) return -1; NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, to && to->tcp_timeout ? (uint32_t)to->tcp_timeout : (uint32_t)orig_ipvs_timeouts.tcp_timeout); NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN, to && to->tcp_fin_timeout ? (uint32_t)to->tcp_fin_timeout : (uint32_t)orig_ipvs_timeouts.tcp_fin_timeout); NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, to && to->udp_timeout ? (uint32_t)to->udp_timeout : (uint32_t)orig_ipvs_timeouts.udp_timeout); return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL); nla_put_failure: nlmsg_free(msg); return -1; } #endif return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_TIMEOUT, (const char *)( to ? to : &orig_ipvs_timeouts), sizeof(ipvs_timeout_t)); } int ipvs_start_daemon(ipvs_daemon_t *dm) { ipvs_func = ipvs_start_daemon; #ifdef LIBIPVS_USE_NL if (try_nl) { struct nlattr *nl_daemon; struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_NEW_DAEMON, 0); if (!msg) return -1; nl_daemon = nla_nest_start(msg, IPVS_CMD_ATTR_DAEMON); if (!nl_daemon) goto nla_put_failure; NLA_PUT_S32(msg, IPVS_DAEMON_ATTR_STATE, dm->user.state); NLA_PUT_STRING(msg, IPVS_DAEMON_ATTR_MCAST_IFN, dm->user.mcast_ifn); NLA_PUT_S32(msg, IPVS_DAEMON_ATTR_SYNC_ID, dm->user.syncid); #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ if (dm->sync_maxlen) NLA_PUT_U16(msg, IPVS_DAEMON_ATTR_SYNC_MAXLEN, dm->sync_maxlen); if (dm->mcast_port) NLA_PUT_U16(msg, IPVS_DAEMON_ATTR_MCAST_PORT, dm->mcast_port); if (dm->mcast_ttl) NLA_PUT_U8(msg, IPVS_DAEMON_ATTR_MCAST_TTL, dm->mcast_ttl); if (dm->mcast_af == AF_INET6) NLA_PUT(msg, IPVS_DAEMON_ATTR_MCAST_GROUP6, sizeof(dm->mcast_group.in6), &dm->mcast_group.in6); else if (dm->mcast_af == AF_INET) NLA_PUT_U32(msg, IPVS_DAEMON_ATTR_MCAST_GROUP, dm->mcast_group.ip); #endif nla_nest_end(msg, nl_daemon); return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL); nla_put_failure: nlmsg_free(msg); return -1; } #endif return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_STARTDAEMON, &dm->user, sizeof(dm->user)); } int ipvs_stop_daemon(ipvs_daemon_t *dm) { ipvs_func = ipvs_stop_daemon; #ifdef LIBIPVS_USE_NL if (try_nl) { struct nlattr *nl_daemon; struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_DEL_DAEMON, 0); if (!msg) return -1; nl_daemon = nla_nest_start(msg, IPVS_CMD_ATTR_DAEMON); if (!nl_daemon) goto nla_put_failure; NLA_PUT_S32(msg, IPVS_DAEMON_ATTR_STATE, dm->user.state); NLA_PUT_S32(msg, IPVS_DAEMON_ATTR_SYNC_ID, dm->user.syncid); nla_nest_end(msg, nl_daemon); return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL); nla_put_failure: nlmsg_free(msg); return -1; } #endif return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_STOPDAEMON, &dm->user, sizeof(dm->user)); } #ifdef _WITH_SNMP_CHECKER_ #ifdef _WITH_LVS_64BIT_STATS_ static void ipvs_copy_stats(ip_vs_stats_t *stats_out, const struct ip_vs_stats_user *stats_in) { stats_out->conns = stats_in->conns; stats_out->inpkts = stats_in->inpkts; stats_out->outpkts = stats_in->outpkts; stats_out->inbytes = stats_in->inbytes; stats_out->outbytes = stats_in->outbytes; stats_out->cps = stats_in->cps; stats_out->inpps = stats_in->inpps; stats_out->outpps = stats_in->outpps; stats_out->inbps = stats_in->inbps; stats_out->outbps = stats_in->outbps; } #endif #ifdef LIBIPVS_USE_NL #ifdef _WITH_LVS_64BIT_STATS_ static int ipvs_parse_stats64(ip_vs_stats_t *stats, struct nlattr *nla) { struct nlattr *attrs[IPVS_STATS_ATTR_MAX + 1]; if (nla_parse_nested(attrs, IPVS_STATS_ATTR_MAX, nla, ipvs_stats64_policy)) return -1; if (!(attrs[IPVS_STATS_ATTR_CONNS] && attrs[IPVS_STATS_ATTR_INPKTS] && attrs[IPVS_STATS_ATTR_OUTPKTS] && attrs[IPVS_STATS_ATTR_INBYTES] && attrs[IPVS_STATS_ATTR_OUTBYTES] && attrs[IPVS_STATS_ATTR_CPS] && attrs[IPVS_STATS_ATTR_INPPS] && attrs[IPVS_STATS_ATTR_OUTPPS] && attrs[IPVS_STATS_ATTR_INBPS] && attrs[IPVS_STATS_ATTR_OUTBPS])) return -1; stats->conns = nla_get_u64(attrs[IPVS_STATS_ATTR_CONNS]); stats->inpkts = nla_get_u64(attrs[IPVS_STATS_ATTR_INPKTS]); stats->outpkts = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTPKTS]); stats->inbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_INBYTES]); stats->outbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTBYTES]); stats->cps = nla_get_u64(attrs[IPVS_STATS_ATTR_CPS]); stats->inpps = nla_get_u64(attrs[IPVS_STATS_ATTR_INPPS]); stats->outpps = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTPPS]); stats->inbps = nla_get_u64(attrs[IPVS_STATS_ATTR_INBPS]); stats->outbps = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTBPS]); return 0; } #endif static int ipvs_parse_stats(ip_vs_stats_t *stats, struct nlattr *nla) { struct nlattr *attrs[IPVS_STATS_ATTR_MAX + 1]; if (nla_parse_nested(attrs, IPVS_STATS_ATTR_MAX, nla, ipvs_stats_policy)) return -1; if (!(attrs[IPVS_STATS_ATTR_CONNS] && attrs[IPVS_STATS_ATTR_INPKTS] && attrs[IPVS_STATS_ATTR_OUTPKTS] && attrs[IPVS_STATS_ATTR_INBYTES] && attrs[IPVS_STATS_ATTR_OUTBYTES] && attrs[IPVS_STATS_ATTR_CPS] && attrs[IPVS_STATS_ATTR_INPPS] && attrs[IPVS_STATS_ATTR_OUTPPS] && attrs[IPVS_STATS_ATTR_INBPS] && attrs[IPVS_STATS_ATTR_OUTBPS])) return -1; stats->conns = nla_get_u32(attrs[IPVS_STATS_ATTR_CONNS]); stats->inpkts = nla_get_u32(attrs[IPVS_STATS_ATTR_INPKTS]); stats->outpkts = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTPKTS]); stats->inbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_INBYTES]); stats->outbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTBYTES]); stats->cps = nla_get_u32(attrs[IPVS_STATS_ATTR_CPS]); stats->inpps = nla_get_u32(attrs[IPVS_STATS_ATTR_INPPS]); stats->outpps = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTPPS]); stats->inbps = nla_get_u32(attrs[IPVS_STATS_ATTR_INBPS]); stats->outbps = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTBPS]); return 0; } static int ipvs_services_parse_cb(struct nl_msg *msg, void *arg) { struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1]; struct nlattr *svc_attrs[IPVS_SVC_ATTR_MAX + 1]; struct ip_vs_get_services_app **getp = PTR_CAST(struct ip_vs_get_services_app *, arg); struct ip_vs_get_services_app *get = *getp; struct ip_vs_flags flags; if (get->user.num_services) { log_message(LOG_INFO, "ipvs_services_parse_cb get->user.num_services = %u != 0", get->user.num_services); return -1; } if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0) return -1; if (!attrs[IPVS_CMD_ATTR_SERVICE]) return -1; if (nla_parse_nested(svc_attrs, IPVS_SVC_ATTR_MAX, attrs[IPVS_CMD_ATTR_SERVICE], ipvs_service_policy)) return -1; memset(&(get->user.entrytable[0]), 0, sizeof(get->user.entrytable[0])); if (!(svc_attrs[IPVS_SVC_ATTR_AF] && (svc_attrs[IPVS_SVC_ATTR_FWMARK] || (svc_attrs[IPVS_SVC_ATTR_PROTOCOL] && svc_attrs[IPVS_SVC_ATTR_ADDR] && svc_attrs[IPVS_SVC_ATTR_PORT])) && svc_attrs[IPVS_SVC_ATTR_SCHED_NAME] && svc_attrs[IPVS_SVC_ATTR_NETMASK] && svc_attrs[IPVS_SVC_ATTR_TIMEOUT] && svc_attrs[IPVS_SVC_ATTR_FLAGS])) return -1; get->user.entrytable[0].af = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_AF]); if (svc_attrs[IPVS_SVC_ATTR_FWMARK]) get->user.entrytable[0].user.fwmark = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_FWMARK]); else { get->user.entrytable[0].user.protocol = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_PROTOCOL]); memcpy(&(get->user.entrytable[0].nf_addr), nla_data(svc_attrs[IPVS_SVC_ATTR_ADDR]), sizeof(get->user.entrytable[0].nf_addr)); get->user.entrytable[0].user.port = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_PORT]); } strcpy_safe(get->user.entrytable[0].user.sched_name, nla_get_string(svc_attrs[IPVS_SVC_ATTR_SCHED_NAME])); if (svc_attrs[IPVS_SVC_ATTR_PE_NAME]) strcpy_safe(get->user.entrytable[0].pe_name, nla_get_string(svc_attrs[IPVS_SVC_ATTR_PE_NAME])); get->user.entrytable[0].user.netmask = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_NETMASK]); get->user.entrytable[0].user.timeout = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_TIMEOUT]); nla_memcpy(&flags, svc_attrs[IPVS_SVC_ATTR_FLAGS], sizeof(flags)); get->user.entrytable[0].user.flags = flags.flags & flags.mask; #ifdef _WITH_LVS_64BIT_STATS_ if (svc_attrs[IPVS_SVC_ATTR_STATS64]) { if (ipvs_parse_stats64(&(get->user.entrytable[0].stats), svc_attrs[IPVS_SVC_ATTR_STATS64]) != 0) return -1; } else if (svc_attrs[IPVS_SVC_ATTR_STATS]) #endif { if (ipvs_parse_stats(&(get->user.entrytable[0].ip_vs_stats), svc_attrs[IPVS_SVC_ATTR_STATS]) != 0) return -1; } get->user.entrytable[0].user.num_dests = 0; get->user.num_services++; return 0; } static int ipvs_dests_parse_cb(struct nl_msg *msg, void *arg) { struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1]; struct nlattr *dest_attrs[IPVS_DEST_ATTR_MAX + 1]; struct ip_vs_get_dests_app **dp = PTR_CAST(struct ip_vs_get_dests_app *, arg); struct ip_vs_get_dests_app *d = PTR_CAST(struct ip_vs_get_dests_app, *dp); unsigned i = d->user.num_dests; struct ip_vs_dest_entry_app *ent; if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0) return -1; if (!attrs[IPVS_CMD_ATTR_DEST]) return -1; if (nla_parse_nested(dest_attrs, IPVS_DEST_ATTR_MAX, attrs[IPVS_CMD_ATTR_DEST], ipvs_dest_policy)) return -1; if (!(dest_attrs[IPVS_DEST_ATTR_ADDR] && dest_attrs[IPVS_DEST_ATTR_PORT] && dest_attrs[IPVS_DEST_ATTR_FWD_METHOD] && dest_attrs[IPVS_DEST_ATTR_WEIGHT] && dest_attrs[IPVS_DEST_ATTR_U_THRESH] && dest_attrs[IPVS_DEST_ATTR_L_THRESH] && dest_attrs[IPVS_DEST_ATTR_ACTIVE_CONNS] && dest_attrs[IPVS_DEST_ATTR_INACT_CONNS] && dest_attrs[IPVS_DEST_ATTR_PERSIST_CONNS])) return -1; if (d->user.num_dests == d->num_entries) { /* There are more RS than we expected. Allow space for another 10. */ d = REALLOC(d, sizeof(*d) + sizeof(ipvs_dest_entry_t) * (d->num_entries += 10)); *dp = d; } ent = &d->user.entrytable[i]; memset(ent, 0, sizeof(*ent)); memcpy(&ent->nf_addr, nla_data(dest_attrs[IPVS_DEST_ATTR_ADDR]), nla_len(dest_attrs[IPVS_DEST_ATTR_ADDR])); ent->user.port = nla_get_u16(dest_attrs[IPVS_DEST_ATTR_PORT]); ent->user.conn_flags = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_FWD_METHOD]); ent->user.weight = nla_get_s32(dest_attrs[IPVS_DEST_ATTR_WEIGHT]); ent->user.u_threshold = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_U_THRESH]); ent->user.l_threshold = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_L_THRESH]); ent->user.activeconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_ACTIVE_CONNS]); ent->user.inactconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_INACT_CONNS]); ent->user.persistconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_PERSIST_CONNS]); #if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY if (dest_attrs[IPVS_DEST_ATTR_ADDR_FAMILY]) ent->af = nla_get_u16(dest_attrs[IPVS_DEST_ATTR_ADDR_FAMILY]); else #endif ent->af = d->af; #ifdef _WITH_LVS_64BIT_STATS_ if (dest_attrs[IPVS_DEST_ATTR_STATS64]) { if (ipvs_parse_stats64(&ent->ip_vs_stats, dest_attrs[IPVS_DEST_ATTR_STATS64]) != 0) return -1; } else if (dest_attrs[IPVS_DEST_ATTR_STATS]) #endif { if (ipvs_parse_stats(&ent->ip_vs_stats, dest_attrs[IPVS_DEST_ATTR_STATS]) != 0) return -1; } d->user.num_dests++; return 0; } #endif /* LIBIPVS_USE_NL */ struct ip_vs_get_dests_app *ipvs_get_dests(__u32 fwmark, __u16 af, __u16 protocol, union nf_inet_addr *addr, __u16 port, unsigned num_dests) { struct ip_vs_get_dests_app *d; struct ip_vs_get_dests *dk; socklen_t len; unsigned i; len = (socklen_t)(sizeof(*d) + sizeof(ipvs_dest_entry_t) * num_dests); if (!(d = MALLOC(len))) return NULL; d->num_entries = num_dests; ipvs_func = ipvs_get_dests; #ifdef LIBIPVS_USE_NL if (try_nl) { struct nl_msg *msg; struct nlattr *nl_service; d->af = af; d->user.num_dests = 0; msg = ipvs_nl_message(IPVS_CMD_GET_DEST, NLM_F_DUMP); if (!msg) goto ipvs_nl_dest_failure; nl_service = nla_nest_start(msg, IPVS_CMD_ATTR_SERVICE); if (!nl_service) goto nla_put_failure; NLA_PUT_U16(msg, IPVS_SVC_ATTR_AF, af); if (fwmark) { NLA_PUT_U32(msg, IPVS_SVC_ATTR_FWMARK, fwmark); } else { NLA_PUT_U16(msg, IPVS_SVC_ATTR_PROTOCOL, protocol); NLA_PUT(msg, IPVS_SVC_ATTR_ADDR, af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr), addr); NLA_PUT_U16(msg, IPVS_SVC_ATTR_PORT, port); } nla_nest_end(msg, nl_service); if (ipvs_nl_send_message(msg, ipvs_dests_parse_cb, &d)) goto ipvs_nl_dest_failure; return d; nla_put_failure: nlmsg_free(msg); ipvs_nl_dest_failure: FREE(d); return NULL; } #endif /* LIBIPVS_USE_NL */ if (af != AF_INET) { errno = EAFNOSUPPORT; FREE(d); return NULL; } len = (socklen_t)(sizeof(*dk) + sizeof(struct ip_vs_dest_entry) * num_dests); if (!(dk = MALLOC(len))) { FREE(d); return NULL; } dk->fwmark = fwmark; dk->protocol = protocol; if (addr) dk->addr = addr->ip; dk->port = port; if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_DESTS, dk, &len) < 0) { FREE(d); FREE(dk); return NULL; } d->user.num_dests = dk->num_dests; for (i = 0; i < dk->num_dests; i++) { d->user.entrytable[i].user = dk->entrytable[i]; d->user.entrytable[i].af = AF_INET; d->user.entrytable[i].nf_addr.ip = d->user.entrytable[i].user.addr; #ifdef _WITH_LVS_64BIT_STATS_ ipvs_copy_stats(&d->user.entrytable[i].ip_vs_stats, &dk->entrytable[i].stats); #endif } FREE(dk); return d; } ipvs_service_entry_t * ipvs_get_service(__u32 fwmark, __u16 af, __u16 protocol, union nf_inet_addr *addr, __u16 port) { ipvs_service_entry_t *svc; socklen_t len; ipvs_func = ipvs_get_service; #ifdef LIBIPVS_USE_NL if (try_nl) { struct ip_vs_get_services_app *get; struct nlattr *nl_service; struct nl_msg *msg; svc = MALLOC(sizeof(*svc)); if (!svc) return NULL; if (!(get = MALLOC(sizeof(*get) + sizeof(ipvs_service_entry_t)))) goto ipvs_get_service_err2; get->user.num_services = 0; msg = ipvs_nl_message(IPVS_CMD_GET_SERVICE, 0); if (!msg) goto ipvs_get_service_err; nl_service = nla_nest_start(msg, IPVS_CMD_ATTR_SERVICE); if (!nl_service) goto nla_put_failure; NLA_PUT_U16(msg, IPVS_SVC_ATTR_AF, af); if (fwmark) { NLA_PUT_U32(msg, IPVS_SVC_ATTR_FWMARK, fwmark); } else { NLA_PUT_U16(msg, IPVS_SVC_ATTR_PROTOCOL, protocol); NLA_PUT(msg, IPVS_SVC_ATTR_ADDR, sizeof(*addr), addr); NLA_PUT_U16(msg, IPVS_SVC_ATTR_PORT, port); } nla_nest_end(msg, nl_service); if (ipvs_nl_send_message(msg, ipvs_services_parse_cb, &get)) goto ipvs_get_service_err; *svc = get->user.entrytable[0]; FREE(get); return svc; nla_put_failure: nlmsg_free(msg); ipvs_get_service_err: FREE(get); ipvs_get_service_err2: FREE(svc); return NULL; } #endif if (af != AF_INET) { errno = EAFNOSUPPORT; return NULL; } len = sizeof(*svc); svc = MALLOC(len); if (!svc) return NULL; svc->user.fwmark = fwmark; svc->af = AF_INET; svc->user.protocol = protocol; svc->user.addr = addr->ip; svc->user.port = port; if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_SERVICE, svc, &len)) { FREE(svc); return NULL; } svc->af = AF_INET; svc->nf_addr.ip = svc->user.addr; svc->pe_name[0] = '\0'; #ifdef _WITH_LVS_64BIT_STATS_ ipvs_copy_stats(&svc->stats, &svc->user.stats); #endif return svc; } #endif /* _WITH_SNMP_CHECKER_ */ void ipvs_close(void) { #ifdef LIBIPVS_USE_NL if (try_nl) { if (sock) { nl_socket_free(sock); sock = NULL; } return; } #endif if (sockfd != -1) { close(sockfd); sockfd = -1; } } static struct table_struct { void *func; int err; const char *message; } table [] = { { ipvs_add_service, EEXIST, "Service already exists" }, { ipvs_add_service, ENOENT, "Scheduler or persistence engine not found" }, { ipvs_update_service, ENOENT, "Scheduler or persistence engine not found" }, { ipvs_add_dest, EEXIST, "Destination already exists" }, { ipvs_update_dest, ENOENT, "No such destination" }, { ipvs_del_dest, ENOENT, "No such destination" }, { ipvs_start_daemon, EEXIST, "Daemon has already run" }, { ipvs_stop_daemon, ESRCH, "No daemon is running" }, { NULL, ESRCH, "No such service" }, { NULL, EPERM, "Permission denied (you must be root)" }, { NULL, EINVAL, "Invalid operation. Possibly wrong module version, address not unicast, ..." }, { NULL, ENOPROTOOPT, "Protocol not available" }, { NULL, ENOMEM, "Memory allocation problem" }, { NULL, EOPNOTSUPP, "Operation not supported with IPv6" }, { NULL, EAFNOSUPPORT, "Operation not supported with specified address family" }, { NULL, EMSGSIZE, "Module is wrong version" }, }; const char * ipvs_strerror(int err) { unsigned int i; for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) { if ((!table[i].func || table[i].func == ipvs_func) && table[i].err == err) return table[i].message; } return strerror(err); } keepalived-2.3.3/keepalived/check/check_print.c0000664000175000017500000000256114714144150015143 /* * 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: Print running LVS/checker state information * * Author: Quentin Armitage * * 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) 2019-2019 Alexandre Cassen, */ #include "config.h" #include #include "logger.h" #include "global_data.h" #include "check_print.h" #include "check_data.h" #include "utils.h" void check_print_data(void) { FILE *fp; fp = open_dump_file("_check"); if (!fp) return; dump_data_check(fp); fclose(fp); } keepalived-2.3.3/keepalived/check/check_file.c0000664000175000017500000001463114745723745014750 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: FILE CHECK. Monitor contents if a file * * Authors: Quentin Armitage, * * 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) 2020-2020 Alexandre Cassen, */ #include "config.h" #include #include "check_file.h" #include "check_data.h" #include "track_file.h" #include "parser.h" #include "logger.h" #include "check_data.h" #include "logger.h" #include "main.h" #include "check_parser.h" static tracked_file_monitor_t *current_tfile; static void free_file_check(checker_t *checker) { FREE(checker); } static void dump_file_check(FILE *fp, const checker_t *checker) { tracked_file_t *tfp = checker->data; conf_write(fp, " Keepalive method = FILE_CHECK"); conf_write(fp, " Tracked file = %s", tfp->fname); conf_write(fp, " Reloaded = %s", tfp->reloaded ? "Yes" : "No"); } static void track_file_handler(const vector_t *strvec) { tracked_file_t *vsf; vsf = find_tracked_file_by_name(strvec_slot(strvec, 1), &check_data->track_files); if (!vsf) { report_config_error(CONFIG_GENERAL_ERROR, "track_file %s not found", strvec_slot(strvec, 1)); return; } current_tfile->file = vsf; } static void file_check_handler(__attribute__((unused)) const vector_t *strvec) { PMALLOC(current_tfile); current_tfile->weight = IPVS_WEIGHT_FAULT; INIT_LIST_HEAD(¤t_tfile->e_list); } static void track_file_weight_handler(const vector_t *strvec) { int weight; bool reverse = false; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "track file weight missing"); return; } if (!read_int_strvec(strvec, 1, &weight, -IPVS_WEIGHT_LIMIT, IPVS_WEIGHT_LIMIT, true)) { report_config_error(CONFIG_GENERAL_ERROR, "weight for track file must be in " "[%d..%d] inclusive. Ignoring...", -IPVS_WEIGHT_LIMIT, IPVS_WEIGHT_LIMIT); return; } if (vector_size(strvec) >= 3) { if (!strcmp(strvec_slot(strvec, 2), "reverse")) reverse = true; else if (!strcmp(strvec_slot(strvec, 2), "noreverse")) reverse = false; else { report_config_error(CONFIG_GENERAL_ERROR, "unknown track file weight option %s - ignoring", strvec_slot(strvec, 2)); return; } } current_tfile->weight = weight; current_tfile->weight_reverse = reverse; } static void file_end_handler(void) { if (!current_tfile->file) { report_config_error(CONFIG_GENERAL_ERROR, "FILE_CHECK has no track_file specified - ignoring"); free_track_file_monitor(current_tfile); return; } if (current_tfile->weight == IPVS_WEIGHT_FAULT) { current_tfile->weight = current_tfile->file->weight; current_tfile->weight_reverse = current_tfile->file->weight_reverse; } list_add_tail(¤t_tfile->e_list, ¤t_rs->track_files); current_tfile = NULL; } void install_file_check_keyword(void) { vpp_t check_ptr; install_keyword("FILE_CHECK", &file_check_handler); check_ptr = install_sublevel(VPP ¤t_tfile); install_keyword("track_file", &track_file_handler); install_keyword("weight", &track_file_weight_handler); install_level_end_handler(&file_end_handler); install_sublevel_end(check_ptr); } static const checker_funcs_t file_checker_funcs = { CHECKER_FILE, free_file_check, dump_file_check, NULL, NULL }; void add_rs_to_track_files(void) { virtual_server_t *vs; real_server_t *rs; tracked_file_monitor_t *tfl; list_for_each_entry(vs, &check_data->vs, e_list) { current_vs = vs; list_for_each_entry(rs, &vs->rs, e_list) { current_rs = rs; list_for_each_entry(tfl, &rs->track_files, e_list) { /* queue new checker - we don't have a compare function since we don't * update file checkers that way on a reload. */ queue_checker(&file_checker_funcs, NULL, tfl->file, NULL, false); current_checker->vs = vs; current_checker->rs = rs; /* There is no concept of the checker running, but we will have * checked the file, so mark it as run. */ current_checker->has_run = true; /* Clear Alpha mode - we know the state of the checker immediately */ current_checker->alpha = false; add_obj_to_track_file(current_checker, tfl, FMT_RS(rs, vs), dump_tracking_rs); } } } current_vs = NULL; current_rs = NULL; } void set_track_file_checkers_down(void) { tracked_file_t *tfl; tracking_obj_t *top; int status; list_for_each_entry(tfl, &check_data->track_files, e_list) { if (tfl->last_status) { list_for_each_entry(top, &tfl->tracking_obj, e_list) { checker_t *checker = top->obj.checker; if (!top->weight || (int64_t)tfl->last_status * top->weight * top->weight_multiplier <= IPVS_WEIGHT_FAULT) { if (reload) { /* This is pretty horrible. At some stage this should * be tidied up so that it works without having to * fudge the values to make update_track_file_status() * work for us. */ status = tfl->last_status; tfl->last_status = 0; process_update_checker_track_file_status(tfl, !status != (top->weight_multiplier == 1) ? IPVS_WEIGHT_FAULT: 0, top); tfl->last_status = status; } else checker->is_up = false; } else if ((int64_t)tfl->last_status * top->weight * top->weight_multiplier <= IPVS_WEIGHT_FAULT && !reload) checker->is_up = false; } } } } void set_track_file_weights(void) { tracked_file_t *tfl; tracking_obj_t *top; list_for_each_entry(tfl, &check_data->track_files, e_list) { if (tfl->last_status) { list_for_each_entry(top, &tfl->tracking_obj, e_list) { checker_t *checker = top->obj.checker; if (top->weight) checker->cur_weight = (int64_t)tfl->last_status * top->weight * top->weight_multiplier; } } } } #ifdef THREAD_DUMP void register_check_file_addresses(void) { register_track_file_inotify_addresses(); } #endif keepalived-2.3.3/keepalived/check/check_api.c0000664000175000017500000004552514661620303014567 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Checkers registration. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include "check_api.h" #include "main.h" #include "parser.h" #include "utils.h" #include "logger.h" #include "bitops.h" #include "global_data.h" #include "keepalived_netlink.h" #include "check_misc.h" #include "check_smtp.h" #include "check_tcp.h" #include "check_http.h" #include "check_ssl.h" #include "check_dns.h" #include "check_ping.h" #include "check_udp.h" #include "check_file.h" #include "ipwrapper.h" #include "check_daemon.h" #ifdef _WITH_BFD_ #include "check_bfd.h" #include "bfd_event.h" #include "bfd_daemon.h" #endif #include "track_file.h" #include "check_parser.h" /* Global vars */ #ifdef _CHECKER_DEBUG_ bool do_checker_debug; #endif checker_t *current_checker; /* free checker data */ void free_checker(checker_t *checker) { list_del_init(&checker->rs_list); (*checker->checker_funcs->free_func) (checker); } /* dump checker data */ static void dump_checker(FILE *fp, const checker_t *checker) { conf_write(fp, " %s -> %s", FMT_VS(checker->vs), FMT_CHK(checker)); conf_write(fp, " Enabled = %s", checker->enabled ? "yes" : "no"); conf_write(fp, " Up = %s", checker->is_up ? "yes" : "no"); conf_write(fp, " Has run = %s", checker->has_run ? "yes" : "no"); conf_write(fp, " Current weight = %d", checker->cur_weight); if (checker->checker_funcs->type != CHECKER_FILE) { conf_write(fp, " Alpha = %s", checker->alpha ? "yes" : "no"); conf_write(fp, " Delay loop = %lu us", checker->delay_loop); conf_write(fp, " Warmup = %lu us", checker->warmup); conf_write(fp, " Retries = %u", checker->retry); if (checker->retry) { conf_write(fp, " Delay before retry = %lu us", checker->delay_before_retry); conf_write(fp, " Retries iterations = %u", checker->retry_it); } conf_write(fp, " Default delay before retry = %lu us", checker->default_delay_before_retry); } conf_write(fp, " Log all failures = %s", checker->log_all_failures ? "yes" : "no"); if (checker->co) { conf_write(fp, " Connection"); dump_connection_opts(fp, checker->co); } (*checker->checker_funcs->dump_func) (fp, checker); } void dump_connection_opts(FILE *fp, const void *data) { const conn_opts_t *conn = data; conf_write(fp, " Dest = %s", inet_sockaddrtopair(&conn->dst)); if (conn->bindto.ss_family) conf_write(fp, " Bind to = %s", inet_sockaddrtopair(&conn->bindto)); if (conn->bind_if[0]) conf_write(fp, " Bind i/f = %s", conn->bind_if); #ifdef _WITH_SO_MARK_ if (conn->fwmark != 0) conf_write(fp, " Mark = %u", conn->fwmark); #endif conf_write(fp, " Timeout = %f", (double)conn->connection_to / TIMER_HZ); if (conn->last_errno) conf_write(fp, " Last errno = %d", conn->last_errno); } /* Queue a checker into the real server's checkers_queue */ void queue_checker(const checker_funcs_t *funcs , thread_func_t launch , void *data , conn_opts_t *co , bool fd_required) { checker_t *checker; /* Set default dst = RS, timeout = default */ if (co) { co->dst = current_rs->addr; co->connection_to = UINT_MAX; } PMALLOC(checker); INIT_LIST_HEAD(&checker->rs_list); checker->checker_funcs = funcs; checker->launch = launch; checker->vs = current_vs; checker->rs = current_rs; checker->data = data; checker->co = co; checker->enabled = true; checker->alpha = -1; checker->delay_loop = ULONG_MAX; checker->warmup = ULONG_MAX; checker->retry = UINT_MAX; checker->delay_before_retry = ULONG_MAX; checker->retry_it = 0; checker->is_up = true; checker->default_delay_before_retry = 1 * TIMER_HZ; checker->default_retry = 1 ; if (fd_required) check_data->num_checker_fd_required++; list_add_tail(&checker->rs_list, ¤t_rs->checkers_list); current_checker = checker; } void dequeue_new_checker(void) { // TODO - queue checker at end, not at start if (!current_checker->is_up) set_checker_state(current_checker, true); free_checker(current_checker); current_checker = NULL; } bool check_conn_opts(conn_opts_t *co) { if (co->dst.ss_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&PTR_CAST(struct sockaddr_in6, &co->dst)->sin6_addr) && !co->bind_if[0]) { report_config_error(CONFIG_GENERAL_ERROR, "Checker link local address %s requires a bind_if", inet_sockaddrtos(&co->dst)); return false; } return true; } bool __attribute__ ((pure)) compare_conn_opts(const conn_opts_t *a, const conn_opts_t *b) { if (a == b) return true; if (!a || !b) return false; if (!sockstorage_equal(&a->dst, &b->dst)) return false; if (!sockstorage_equal(&a->bindto, &b->bindto)) return false; if (strcmp(a->bind_if, b->bind_if)) return false; if (a->connection_to != b->connection_to) return false; #ifdef _WITH_SO_MARK_ if (a->fwmark != b->fwmark) return false; #endif return true; } void checker_set_dst_port(sockaddr_t *dst, uint16_t port) { /* NOTE: we are relying on the offset of sin_port and sin6_port being * the same if an IPv6 address is specified after the port */ if (dst->ss_family == AF_INET6) { struct sockaddr_in6 *addr6 = PTR_CAST(struct sockaddr_in6, dst); addr6->sin6_port = port; } else if (dst->ss_family == AF_UNSPEC && offsetof(struct sockaddr_in6, sin6_port) != offsetof(struct sockaddr_in, sin_port)) { log_message(LOG_INFO, "BUG: checker_set_dst_port() in/in6 port offsets differ"); } else { struct sockaddr_in *addr4 = PTR_CAST(struct sockaddr_in, dst); addr4->sin_port = port; } } /* "connect_ip" keyword */ static void co_ip_handler(const vector_t *strvec) { conn_opts_t *co = current_checker->co; if (inet_stosockaddr(strvec_slot(strvec, 1), NULL, &co->dst)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid connect_ip address %s - ignoring", strvec_slot(strvec, 1)); else if (co->bindto.ss_family != AF_UNSPEC && co->bindto.ss_family != co->dst.ss_family) { report_config_error(CONFIG_GENERAL_ERROR, "connect_ip address %s does not match address family of bindto - skipping", strvec_slot(strvec, 1)); co->dst.ss_family = AF_UNSPEC; } } /* "connect_port" keyword */ static void co_port_handler(const vector_t *strvec) { conn_opts_t *co = current_checker->co; unsigned port; if (!read_unsigned_strvec(strvec, 1, &port, 1, 65535, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid checker connect_port '%s'", strvec_slot(strvec, 1)); return; } checker_set_dst_port(&co->dst, htons(port)); } /* "bindto" keyword */ static void co_srcip_handler(const vector_t *strvec) { conn_opts_t *co = current_checker->co; if (inet_stosockaddr(strvec_slot(strvec, 1), NULL, &co->bindto)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid bindto address %s - ignoring", strvec_slot(strvec, 1)); else if (co->dst.ss_family != AF_UNSPEC && co->dst.ss_family != co->bindto.ss_family) { report_config_error(CONFIG_GENERAL_ERROR, "bindto address %s does not match address family of connect_ip - skipping", strvec_slot(strvec, 1)); co->bindto.ss_family = AF_UNSPEC; } } /* "bind_port" keyword */ static void co_srcport_handler(const vector_t *strvec) { conn_opts_t *co = current_checker->co; unsigned port; if (!read_unsigned_strvec(strvec, 1, &port, 1, 65535, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid checker bind_port '%s'", strvec_slot(strvec, 1)); return; } checker_set_dst_port(&co->bindto, htons(port)); } /* "bind_if" keyword */ static void co_srcif_handler(const vector_t *strvec) { // This is needed for link local IPv6 bindto address conn_opts_t *co = current_checker->co; if (strlen(strvec_slot(strvec, 1)) > sizeof(co->bind_if) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "Interface name %s is too long - ignoring", strvec_slot(strvec, 1)); return; } strcpy(co->bind_if, strvec_slot(strvec, 1)); } /* "connect_timeout" keyword */ static void co_timeout_handler(const vector_t *strvec) { conn_opts_t *co = current_checker->co; unsigned long timer; if (!read_timer(strvec, 1, &timer, 1, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "connect_timeout %s invalid - ignoring", strvec_slot(strvec, 1)); return; } co->connection_to = timer; } #ifdef _WITH_SO_MARK_ /* "fwmark" keyword */ static void co_fwmark_handler(const vector_t *strvec) { conn_opts_t *co = current_checker->co; unsigned fwmark; if (!read_unsigned_strvec(strvec, 1, &fwmark, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid fwmark connection value '%s'", strvec_slot(strvec, 1)); return; } co->fwmark = fwmark; } #endif static void retry_handler(const vector_t *strvec) { checker_t *checker = current_checker; unsigned retry; if (!read_unsigned_strvec(strvec, 1, &retry, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid retry connection value '%s'", strvec_slot(strvec, 1)); return; } checker->retry = retry; } static void delay_before_retry_handler(const vector_t *strvec) { checker_t *checker = current_checker; unsigned long delay; if (!read_timer(strvec, 1, &delay, 0, 0, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid delay_before_retry connection value '%s'", strvec_slot(strvec, 1)); return; } checker->delay_before_retry = delay; } /* "warmup" keyword */ static void warmup_handler(const vector_t *strvec) { checker_t *checker = current_checker; unsigned long warmup; if (!read_timer(strvec, 1, &warmup, 0, 0, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid warmup connection value '%s'", strvec_slot(strvec, 1)); return; } checker->warmup = warmup; } static void delay_handler(const vector_t *strvec) { checker_t *checker = current_checker; unsigned long delay_loop; if (!read_timer(strvec, 1, &delay_loop, 1, 0, true)) { report_config_error(CONFIG_GENERAL_ERROR, "delay_loop '%s' is invalid - ignoring", strvec_slot(strvec, 1)); return; } checker->delay_loop = delay_loop; } static void alpha_handler(const vector_t *strvec) { checker_t *checker = current_checker; 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; } } checker->alpha = res; } static void log_all_failures_handler(const vector_t *strvec) { checker_t *checker = current_checker; 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 log_all_failures parameter %s", strvec_slot(strvec, 1)); return; } } checker->log_all_failures = res; } void install_checker_common_keywords(bool connection_keywords) { if (connection_keywords) { install_keyword("connect_ip", &co_ip_handler); install_keyword("connect_port", &co_port_handler); install_keyword("bindto", &co_srcip_handler); install_keyword("bind_port", &co_srcport_handler); install_keyword("bind_if", &co_srcif_handler); install_keyword("connect_timeout", &co_timeout_handler); #ifdef _WITH_SO_MARK_ install_keyword("fwmark", &co_fwmark_handler); #endif } install_keyword("retry", &retry_handler); install_keyword("delay_before_retry", &delay_before_retry_handler); install_keyword("warmup", &warmup_handler); install_keyword("delay_loop", &delay_handler); install_keyword("alpha", &alpha_handler); install_keyword("log_all_failures", &log_all_failures_handler); } /* dump the checkers */ void dump_checkers(FILE *fp) { virtual_server_t *vs; real_server_t *rs; checker_t *checker; bool dumped_header = false; list_for_each_entry(vs, &check_data->vs, e_list) { list_for_each_entry(rs, &vs->rs, e_list) { list_for_each_entry(checker, &rs->checkers_list, rs_list) { if (!dumped_header) { conf_write(fp, "------< Health checkers >------"); dumped_header = true; } dump_checker(fp, checker); } } } } /* release the checkers for a real server */ void free_rs_checkers(const real_server_t *rs) { checker_t *checker, *checker_tmp; list_for_each_entry_safe(checker, checker_tmp, &rs->checkers_list, rs_list) free_checker(checker); } /* register checkers to the global I/O scheduler */ void register_checkers_thread(void) { virtual_server_t *vs; real_server_t *rs; checker_t *checker; unsigned long warmup; list_for_each_entry(vs, &check_data->vs, e_list) { list_for_each_entry(rs, &vs->rs, e_list) { list_for_each_entry(checker, &rs->checkers_list, rs_list) { if (checker->launch) { if (checker->vs->ha_suspend && !checker->vs->ha_suspend_addr_count) checker->enabled = false; if (!checker->enabled || !checker->has_run) log_message(LOG_INFO, "%sctivating healthchecker for service %s for VS %s" , checker->enabled ? "A" : "Dea" , FMT_RS(checker->rs, checker->vs) , FMT_VS(checker->vs)); /* wait for a random timeout to begin checker thread. It helps avoiding multiple simultaneous checks to the same RS. */ warmup = checker->warmup; if (warmup) { /* coverity[dont_call] */ warmup = warmup * (unsigned)random() / RAND_MAX; } thread_add_timer(master, checker->launch, checker, BOOTSTRAP_DELAY + warmup); } } } } #ifdef _WITH_BFD_ log_message(LOG_INFO, "Activating BFD healthchecker"); /* We need to always enable this, since the bfd process may write to the pipe, and we * need to ensure that messages are stripped out. */ start_bfd_monitoring(master); #endif } /* Sync checkers activity with netlink kernel reflection */ static bool __attribute__ ((pure)) addr_matches(const virtual_server_t *vs, void *address) { virtual_server_group_entry_t *vsg_entry; struct in_addr mask_addr = {0}; struct in6_addr mask_addr6 = {{{0}}}; unsigned addr_base; const void *addr; if (vs->vsg) return false; if (vs->addr.ss_family != AF_UNSPEC) { if (vs->addr.ss_family == AF_INET6) addr = (const void *)&PTR_CAST_CONST(struct sockaddr_in6, &vs->addr)->sin6_addr; else addr = (const void *)&PTR_CAST_CONST(struct sockaddr_in, &vs->addr)->sin_addr; return inaddr_equal(vs->addr.ss_family, addr, address); } if (!vs->vsg || list_empty(&vs->vsg->addr_range)) return false; if (vs->af == AF_INET) { mask_addr = *PTR_CAST(struct in_addr, address); addr_base = ntohl(mask_addr.s_addr) & 0xFF; mask_addr.s_addr &= htonl(0xFFFFFF00); } else { mask_addr6 = *PTR_CAST(struct in6_addr, address); addr_base = ntohs(mask_addr6.s6_addr16[7]); mask_addr6.s6_addr16[7] = 0; } list_for_each_entry(vsg_entry, &vs->vsg->addr_range, e_list) { uint32_t ra_base, ra_end; if (!inet_sockaddrcmp(&vsg_entry->addr, &vsg_entry->addr_end)) { if (vsg_entry->addr.ss_family == AF_INET6) addr = (void *)&PTR_CAST(struct sockaddr_in6, &vsg_entry->addr)->sin6_addr; else addr = (void *)&PTR_CAST(struct sockaddr_in, &vsg_entry->addr)->sin_addr; if (inaddr_equal(vsg_entry->addr.ss_family, addr, address)) return true; continue; } if (vsg_entry->addr.ss_family == AF_INET) { struct in_addr ra; ra_base = ntohl(PTR_CAST(struct sockaddr_in, &vsg_entry->addr)->sin_addr.s_addr) & 0xFF; ra_end = ntohl(PTR_CAST(struct sockaddr_in, &vsg_entry->addr_end)->sin_addr.s_addr) & 0xFF; if (addr_base < ra_base || addr_base > ra_end) continue; ra = PTR_CAST(struct sockaddr_in, &vsg_entry->addr)->sin_addr; ra.s_addr &= htonl(0xFFFFFF00); if (ra.s_addr != mask_addr.s_addr) continue; } else { struct in6_addr ra; ra_base = ntohs(PTR_CAST(struct sockaddr_in6, &vsg_entry->addr)->sin6_addr.s6_addr16[7]); ra_end = ntohs(PTR_CAST(struct sockaddr_in6, &vsg_entry->addr_end)->sin6_addr.s6_addr16[7]); if (addr_base < ra_base || addr_base > ra_end) continue; ra = PTR_CAST(struct sockaddr_in6, &vsg_entry->addr)->sin6_addr; ra.s6_addr16[7] = 0; if (!inaddr_equal(AF_INET6, &ra, &mask_addr6)) continue; } return true; } return false; } void update_checker_activity(sa_family_t family, void *address, bool enable) { checker_t *checker; virtual_server_t *vs; real_server_t *rs; char addr_str[INET6_ADDRSTRLEN]; bool address_logged = false; if (__test_bit(LOG_ADDRESS_CHANGES, &debug)) { inet_ntop(family, address, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Netlink reflector reports IP %s %s" , addr_str, (enable) ? "added" : "removed"); address_logged = true; } if (!using_ha_suspend) return; /* Check if any of the virtual servers are using this address, and have ha_suspend */ list_for_each_entry(vs, &check_data->vs, e_list) { if (!vs->ha_suspend) continue; /* If there is no address configured, the family will be AF_UNSPEC */ if (vs->af != family) continue; if (!addr_matches(vs, address)) continue; if (!address_logged && __test_bit(LOG_DETAIL_BIT, &debug)) { inet_ntop(family, address, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Netlink reflector reports IP %s %s" , addr_str, (enable) ? "added" : "removed"); } address_logged = true; /* If we have that same address (IPv6 link local) on multiple interfaces, * we want to count them multiple times so that we only suspend the checkers * if they are all deleted */ if (enable) vs->ha_suspend_addr_count++; else vs->ha_suspend_addr_count--; /* Processing Healthcheckers queue for this vs */ list_for_each_entry(rs, &vs->rs, e_list) { list_for_each_entry(checker, &rs->checkers_list, rs_list) { if (enable != checker->enabled && (enable || vs->ha_suspend_addr_count == 0)) { log_message(LOG_INFO, "%sing healthchecker for service %s for VS %s", !checker->enabled ? "Activat" : "Suspend", FMT_RS(checker->rs, checker->vs), FMT_VS(checker->vs)); checker->enabled = enable; } } } } } /* Install checkers keywords */ void install_checkers_keyword(void) { install_misc_check_keyword(); install_smtp_check_keyword(); install_tcp_check_keyword(); install_ping_check_keyword(); install_udp_check_keyword(); install_http_check_keyword(); install_ssl_check_keyword(); install_dns_check_keyword(); install_file_check_keyword(); #ifdef _WITH_BFD_ install_bfd_check_keyword(); #endif } keepalived-2.3.3/keepalived/check/check_dns.c0000664000175000017500000003435414661620303014600 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: DNS checker * * Author: Masanobu Yasui, * Masaya Yamamoto, * * 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) 2016 KLab Inc. * Copyright (C) 2016-2017 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include "check_dns.h" #include "check_api.h" #include "memory.h" #include "global_data.h" #include "ipwrapper.h" #include "logger.h" #include "smtp.h" #include "utils.h" #include "parser.h" #include "layer4.h" #include "scheduler.h" #include "check_parser.h" const dns_type_t DNS_TYPE[] = { {DNS_TYPE_A, "A"}, {DNS_TYPE_NS, "NS"}, {DNS_TYPE_CNAME, "CNAME"}, {DNS_TYPE_SOA, "SOA"}, {DNS_TYPE_MX, "MX"}, {DNS_TYPE_TXT, "TXT"}, {DNS_TYPE_AAAA, "AAAA"}, {DNS_TYPE_RRSIG, "RRSIG"}, {DNS_TYPE_DNSKEY, "DNSKEY"}, {0, NULL} }; static void dns_connect_thread(thread_ref_t); static void dns_send_thread(thread_ref_t); static uint16_t __attribute__ ((pure)) dns_type_lookup(const char *label) { const dns_type_t *t; for (t = DNS_TYPE; t->type; t++) { if (!strcasecmp(label, t->label)) { return t->type; } } return 0; } static const char * __attribute__ ((pure)) dns_type_name(uint16_t type) { const dns_type_t *t; for (t = DNS_TYPE; t->type; t++) { if (type == t->type) { return t->label; } } return "(unknown)"; } static void __attribute__ ((format (printf, 3, 4))) dns_log_message(thread_ref_t thread, int level, const char *fmt, ...) { char buf[MAX_LOG_MSG]; va_list args; checker_t *checker = THREAD_ARG(thread); va_start(args, fmt); vsnprintf(buf, sizeof (buf), fmt, args); va_end(args); log_message(level, "DNS_CHECK (%s) %s", FMT_CHK(checker), buf); } static int __attribute__ ((format (printf, 3, 4))) dns_final(thread_ref_t thread, bool error, const char *fmt, ...) { char buf[MAX_LOG_MSG]; va_list args; int len; bool checker_was_up; bool rs_was_alive; checker_t *checker = THREAD_ARG(thread); #ifdef _CHECKER_DEBUG_ if (do_checker_debug) dns_log_message(thread, LOG_DEBUG, "final error=%d attempts=%u retry=%u", error, checker->retry_it, checker->retry); #endif if (thread->type != THREAD_READY_TIMER) thread_close_fd(thread); if (error) { if (checker->is_up || !checker->has_run) { if (fmt && (global_data->checker_log_all_failures || checker->log_all_failures || checker->retry_it >= checker->retry)) { va_start(args, fmt); len = vsnprintf(buf, sizeof (buf), fmt, args); va_end(args); if (checker->has_run && checker->retry_it >= checker->retry ) snprintf(buf + len, sizeof(buf) - len, " after %u retries", checker->retry); dns_log_message(thread, LOG_INFO, "%s", buf); } if (checker->retry_it < checker->retry) { checker->retry_it++; checker->has_run = true; thread_add_timer(thread->master, dns_connect_thread, checker, checker->delay_before_retry); return 0; } checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(DOWN, checker); if (checker_was_up && checker->rs->smtp_alert && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> DNS_CHECK: failed on service <="); } } else { if (!checker->is_up || !checker->has_run) { checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(UP, checker); if (!checker_was_up && checker->rs->smtp_alert && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> DNS_CHECK: succeed on service <="); } } checker->retry_it = 0; thread_add_timer(thread->master, dns_connect_thread, checker, checker->delay_loop); return 0; } static void dns_recv_thread(thread_ref_t thread) { unsigned long timeout; ssize_t ret; char rbuf[DNS_BUFFER_SIZE] __attribute__((aligned(__alignof__(dns_header_t)))); dns_header_t *s_header, *r_header; int flags, rcode; checker_t *checker = THREAD_ARG(thread); dns_check_t *dns_check = CHECKER_ARG(checker); if (thread->type == THREAD_READ_TIMEOUT) { dns_final(thread, true, "read timeout from socket"); return; } timeout = timer_long(thread->sands) - timer_long(time_now); ret = recv(thread->u.f.fd, rbuf, sizeof (rbuf), 0); if (ret == -1) { if (check_EAGAIN(errno) || check_EINTR(errno)) { thread_add_read(thread->master, dns_recv_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } dns_final(thread, true, "failed to read socket; errno %d (%s)", errno, strerror(errno)); return; } if (ret < (ssize_t) sizeof (r_header)) { #ifdef _CHECKER_DEBUG_ if (do_checker_debug) dns_log_message(thread, LOG_DEBUG, "too small message. (%zd bytes)", ret); #endif thread_add_read(thread->master, dns_recv_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } s_header = PTR_CAST(dns_header_t , dns_check->sbuf); r_header = PTR_CAST(dns_header_t , rbuf); if (s_header->id != r_header->id) { #ifdef _CHECKER_DEBUG_ if (do_checker_debug) dns_log_message(thread, LOG_DEBUG, "ID does not match. (%04x != %04x)", ntohs(s_header->id), ntohs(r_header->id)); #endif thread_add_read(thread->master, dns_recv_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } flags = ntohs(r_header->flags); if (!DNS_QR(flags)) { #ifdef _CHECKER_DEBUG_ if (do_checker_debug) dns_log_message(thread, LOG_DEBUG, "receive query message?"); #endif thread_add_read(thread->master, dns_recv_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } if ((rcode = DNS_RC(flags)) != 0) { dns_final(thread, true, "read error occurred. (rcode = %d)", rcode); return; } /* success */ dns_final(thread, false, NULL); } #define APPEND16(x, y) do { \ *PTR_CAST(uint16_t, (x)) = htons(y); \ (x) = (uint8_t *) (x) + 2; \ } while(0) static void dns_make_query(thread_ref_t thread) { uint16_t flags = 0; uint8_t *p; const char *s, *e; size_t n; checker_t *checker = THREAD_ARG(thread); dns_check_t *dns_check = CHECKER_ARG(checker); dns_header_t *header = PTR_CAST(dns_header_t, dns_check->sbuf); DNS_SET_RD(flags, 1); /* Recursion Desired */ /* coverity[dont_call] */ header->id = random(); header->flags = htons(flags); header->qdcount = htons(1); header->ancount = htons(0); header->nscount = htons(0); header->arcount = htons(0); p = PTR_CAST(uint8_t, header + 1); /* QNAME */ for (s = dns_check->name; *s; s = *e ? ++e : e) { if (!(e = strchr(s, '.'))) { e = s + strlen(s); } n = (size_t)(e - s); *(p++) = (uint8_t)n; memcpy(p, s, n); p += n; } if (dns_check->name[0] != '.' || dns_check->name[1] != '\0') *(p++) = 0; APPEND16(p, dns_check->type); APPEND16(p, 1); /* IN */ dns_check->slen = (size_t)(p - PTR_CAST(uint8_t, header)); } static void dns_send(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); dns_check_t *dns_check = CHECKER_ARG(checker); unsigned long timeout; ssize_t ret; timeout = timer_long(thread->sands) - timer_long(time_now); /* Handle time_now > thread->sands (check for underflow) */ if (timeout > checker->co->connection_to) timeout = 0; ret = send(thread->u.f.fd, dns_check->sbuf, dns_check->slen, 0); if (ret == -1) { if (check_EAGAIN(errno) || check_EINTR(errno)) { thread_add_write(thread->master, dns_send_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } dns_final(thread, true, "failed to write socket."); return; } if (ret != (ssize_t) dns_check->slen) { dns_final(thread, true, "failed to write all of the datagram."); return; } thread_add_read(thread->master, dns_recv_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } static void dns_send_thread(thread_ref_t thread) { if (thread->type == THREAD_WRITE_TIMEOUT) { dns_final(thread, true, "write timeout to socket."); return; } dns_send(thread); } static void dns_check_thread(thread_ref_t thread) { int status; if (thread->type == THREAD_WRITE_TIMEOUT) { dns_final(thread, true, "write timeout to socket."); return; } status = socket_state(thread, dns_check_thread, 0); /* If status = connect_in_progress, next thread is already registered. * If it is connect_success, the fd is still open. * Otherwise we have a real connection error or connection timeout. */ switch (status) { case connect_error: dns_final(thread, true, "connection error."); break; case connect_timeout: dns_final(thread, true, "connection timeout."); break; case connect_fail: dns_final(thread, true, "connection failure."); break; case connect_success: dns_make_query(thread); dns_send(thread); /* Cancel the write after the read is added to avoid the * file descriptor being removed */ thread_del_write(thread); break; } } static void dns_connect_thread(thread_ref_t thread) { int fd, status; thread_t thread_fd; checker_t *checker = THREAD_ARG(thread); conn_opts_t *co = checker->co; if (!checker->enabled) { thread_add_timer(thread->master, dns_connect_thread, checker, checker->delay_loop); return; } if ((fd = socket(co->dst.ss_family, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP)) == -1) { dns_log_message(thread, LOG_INFO, "failed to create socket. Rescheduling."); thread_add_timer(thread->master, dns_connect_thread, checker, checker->delay_loop); return; } status = socket_bind_connect(fd, co); if (status == connect_success) { thread_fd = *thread; thread_fd.u.f.fd = fd; thread_fd.sands = timer_add_long(time_now, co->connection_to); dns_make_query(&thread_fd); dns_send(&thread_fd); return; } if (status == connect_fail) { close(fd); dns_final(thread, true, "network unreachable for %s", inet_sockaddrtopair(&co->dst)); return; } /* handle connection status & register check worker thread */ if (socket_connection_state(fd, status, thread, dns_check_thread, co->connection_to, 0)) { close(fd); dns_log_message(thread, LOG_INFO, "UDP socket bind failed. Rescheduling."); thread_add_timer(thread->master, dns_connect_thread, checker, checker->delay_loop); } } static void free_dns_check(checker_t *checker) { dns_check_t *dns_check = checker->data; FREE_CONST(dns_check->name); FREE(checker->co); FREE(checker->data); FREE(checker); } static void dump_dns_check(FILE *fp, const checker_t *checker) { const dns_check_t *dns_check = checker->data; conf_write(fp, " Keepalive method = DNS_CHECK"); conf_write(fp, " Type = %s", dns_type_name(dns_check->type)); conf_write(fp, " Name = %s", dns_check->name); } static bool compare_dns_check(const checker_t *old_c, checker_t *new_c) { const dns_check_t *old = old_c->data; const dns_check_t *new = new_c->data; if (!compare_conn_opts(old_c->co, new_c->co)) return false; if (old->type != new->type) return false; if (strcmp(old->name, new->name) != 0) return false; return true; } static const checker_funcs_t dns_checker_funcs = { CHECKER_DNS, free_dns_check, dump_dns_check, compare_dns_check, NULL }; static void dns_check_handler(__attribute__((unused)) const vector_t *strvec) { dns_check_t *dns_check; PMALLOC(dns_check); dns_check->type = DNS_DEFAULT_TYPE; queue_checker(&dns_checker_funcs, dns_connect_thread, dns_check, CHECKER_NEW_CO(), true); /* Set the non-standard retry time */ current_checker->default_retry = DNS_DEFAULT_RETRY; current_checker->default_delay_before_retry = 0; /* This will default to delay_loop */ } static void dns_type_handler(const vector_t *strvec) { dns_check_t *dns_check = current_checker->data; uint16_t dns_type; dns_type = dns_type_lookup(strvec_slot(strvec, 1)); if (!dns_type) report_config_error(CONFIG_GENERAL_ERROR, "Unknown DNS check type %s - ignoring", strvec_slot(strvec, 1)); else dns_check->type = dns_type; } static void dns_name_handler(const vector_t *strvec) { dns_check_t *dns_check = current_checker->data; const char *name; bool name_invalid = false; const char *p; if (dns_check->name) { report_config_error(CONFIG_GENERAL_ERROR, "DNS_CHECK name already specified - ignoring"); return; } /* Check name does not have an empty label */ name = strvec_slot(strvec, 1); if (name[0] == '.' && name[1] != '\0') name_invalid = true; else { for (p = name; p; p = strchr(p + 1, '.')) { if (p[1] == '.') { name_invalid = true; break; } } } if (name_invalid) { report_config_error(CONFIG_GENERAL_ERROR, "DNS_CHECK name '%s' has empty label - ignoring", name); return; } dns_check->name = STRDUP(name); } static void dns_check_end(void) { dns_check_t *dns_check = current_checker->data; if (!check_conn_opts(current_checker->co)) { dequeue_new_checker(); return; } if (!dns_check->name) dns_check->name = STRDUP(DNS_DEFAULT_NAME); } void install_dns_check_keyword(void) { vpp_t check_ptr; install_keyword("DNS_CHECK", &dns_check_handler); check_ptr = install_sublevel(VPP ¤t_checker); install_checker_common_keywords(true); install_keyword("type", &dns_type_handler); install_keyword("name", &dns_name_handler); install_level_end_handler(dns_check_end); install_sublevel_end(check_ptr); } #ifdef THREAD_DUMP void register_check_dns_addresses(void) { register_thread_address("dns_check_thread", dns_check_thread); register_thread_address("dns_connect_thread", dns_connect_thread); register_thread_address("dns_recv_thread", dns_recv_thread); register_thread_address("dns_send_thread", dns_send_thread); } #endif keepalived-2.3.3/keepalived/check/Makefile.am0000664000175000017500000000210514023235246014534 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2018 Alexandre Cassen, AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib AM_CPPFLAGS += $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libcheck.a libcheck_a_SOURCES = \ check_daemon.c check_data.c check_parser.c \ check_api.c check_tcp.c check_http.c check_ssl.c check_genhash.c \ check_smtp.c check_misc.c check_dns.c check_print.c \ ipwrapper.c ipvswrapper.c libipvs.c check_udp.c check_ping.c \ check_file.c EXTRA_libcheck_a_SOURCES = libcheck_a_LIBADD = if SNMP_CHECKER libcheck_a_LIBADD += check_snmp.o EXTRA_libcheck_a_SOURCES += check_snmp.c endif if NFTABLES libcheck_a_LIBADD += check_nftables.o EXTRA_libcheck_a_SOURCES += check_nftables.c endif if WITH_BFD libcheck_a_LIBADD += check_bfd.o EXTRA_libcheck_a_SOURCES += check_bfd.c endif MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ keepalived-2.3.3/keepalived/check/check_udp.c0000664000175000017500000002432514661620303014601 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: UDP checker. * * Author: Jie Liu, * * 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) 2019-2019 Alexandre Cassen, */ #include "config.h" /* system includes */ #include #include /* local includes */ #include "scheduler.h" #include "check_udp.h" #include "check_api.h" #include "memory.h" #include "ipwrapper.h" #include "layer4.h" #include "logger.h" #include "global_data.h" #include "smtp.h" #include "utils.h" #include "parser.h" #include "check_parser.h" static void udp_connect_thread(thread_ref_t); /* Configuration stream handling */ static void free_udp_check(checker_t *checker) { udp_check_t *udp_check = CHECKER_ARG(checker); FREE_PTR(udp_check->payload); FREE_PTR(udp_check->reply_data); FREE_PTR(udp_check->reply_mask); FREE(checker->co); FREE(checker->data); FREE(checker); } static void dump_udp_check(FILE *fp, const checker_t *checker) { udp_check_t *udp_check = CHECKER_ARG(checker); conf_write(fp, " Keepalive method = UDP_CHECK"); if (udp_check->payload) conf_write(fp, " Payload len = %u", udp_check->payload_len); else conf_write(fp, " Payload specified = no"); conf_write(fp, " Require reply = %s", udp_check->require_reply ? "yes" : "no"); if (udp_check->require_reply) { conf_write(fp, " Min reply length = %u", udp_check->min_reply_len); conf_write(fp, " Max reply length = %u", udp_check->max_reply_len); conf_write(fp, " Reply data len = %u", udp_check->reply_len); if (udp_check->reply_data) conf_write(fp, " Reply data mask = %s", udp_check->reply_mask ? "yes" : "no"); } } static bool compare_udp_check(const checker_t *a, checker_t *b) { return compare_conn_opts(a->co, b->co); } static const checker_funcs_t udp_checker_funcs = { CHECKER_UDP, free_udp_check, dump_udp_check, compare_udp_check, NULL }; static void udp_check_handler(__attribute__((unused)) const vector_t *strvec) { udp_check_t *udp_check = MALLOC(sizeof (udp_check_t)); udp_check->min_reply_len = 0; udp_check->max_reply_len = UINT8_MAX; /* queue new checker */ queue_checker(&udp_checker_funcs, udp_connect_thread, udp_check, CHECKER_NEW_CO(), true); } static void payload_handler(const vector_t *strvec) { udp_check_t *udp_check = current_checker->data; char *hex_str; if (vector_size(strvec) == 1) { report_config_error(CONFIG_GENERAL_ERROR, "UDP_CHECK payload requires a payload"); return; } hex_str = make_strvec_str(strvec, 1); udp_check->payload_len = read_hex_str(hex_str, &udp_check->payload, NULL); if (!udp_check->payload_len) report_config_error(CONFIG_GENERAL_ERROR, "Invalid hex string for UDP_CHECK payload"); FREE_ONLY(hex_str); } static void require_reply_handler(const vector_t *strvec) { udp_check_t *udp_check = current_checker->data; char *hex_str; udp_check->require_reply = true; if (vector_size(strvec) == 1) return; hex_str = make_strvec_str(strvec, 1); udp_check->reply_len = read_hex_str(hex_str, &udp_check->reply_data, &udp_check->reply_mask); if (!udp_check->reply_len) report_config_error(CONFIG_GENERAL_ERROR, "Invalid hex string for UDP_CHECK reply"); FREE_ONLY(hex_str); } static void min_length_handler(const vector_t *strvec) { udp_check_t *udp_check = current_checker->data; unsigned len; if (!read_unsigned_strvec(strvec, 1, &len, 0, UINT16_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "UDP_CHECK min length %s not valid - must be between 0 & %d", strvec_slot(strvec, 1), UINT16_MAX); return; } udp_check->min_reply_len = len; } static void max_length_handler(const vector_t *strvec) { udp_check_t *udp_check = current_checker->data; unsigned len; if (!read_unsigned_strvec(strvec, 1, &len, 0, UINT16_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "UDP_CHECK max length %s not valid - must be between 0 & %d", strvec_slot(strvec, 1), UINT16_MAX); return; } udp_check->max_reply_len = len; } static void udp_check_end_handler(void) { udp_check_t *udp_check = current_checker->data; if (!check_conn_opts(current_checker->co)) { dequeue_new_checker(); return; } if (udp_check->min_reply_len > udp_check->max_reply_len) report_config_error(CONFIG_GENERAL_ERROR, "UDP_CHECK min_reply length %d > max_reply_length %d - will always fail", udp_check->min_reply_len, udp_check->max_reply_len); } void install_udp_check_keyword(void) { vpp_t check_ptr; /* We don't want some common keywords */ install_keyword("UDP_CHECK", &udp_check_handler); check_ptr = install_sublevel(VPP ¤t_checker); install_checker_common_keywords(true); install_keyword("payload", &payload_handler); install_keyword("require_reply", &require_reply_handler); install_keyword("min_reply_length", &min_length_handler); install_keyword("max_reply_length", &max_length_handler); install_level_end_handler(udp_check_end_handler); install_sublevel_end(check_ptr); } static void udp_epilog(thread_ref_t thread, bool is_success) { checker_t *checker; unsigned long delay; bool checker_was_up; bool rs_was_alive; checker = THREAD_ARG(thread); delay = checker->delay_loop; if (is_success || ((checker->is_up || !checker->has_run) && checker->retry_it >= checker->retry)) { checker->retry_it = 0; if (is_success && (!checker->is_up || !checker->has_run)) { log_message(LOG_INFO, "UDP connection to %s success." , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(UP, checker); if (checker->rs->smtp_alert && !checker_was_up && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> UDP CHECK succeed on service <="); } else if (!is_success && (checker->is_up || !checker->has_run)) { if (checker->retry && checker->has_run) log_message(LOG_INFO , "UDP_CHECK on service %s failed after %u retries." , FMT_CHK(checker) , checker->retry); else log_message(LOG_INFO , "UDP_CHECK on service %s failed." , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(DOWN, checker); if (checker->rs->smtp_alert && checker_was_up && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> UDP CHECK failed on service <="); } } else if (checker->is_up) { delay = checker->delay_before_retry; ++checker->retry_it; } checker->has_run = true; thread_add_timer(thread->master, udp_connect_thread, checker, delay); } static bool check_udp_reply(const uint8_t *recv_data, size_t len, const udp_check_t *udp_check) { unsigned i; unsigned check_len; if (len < udp_check->min_reply_len || len > udp_check->max_reply_len) return true; /* We only checker lesser of len and udp_check->reply_len octets */ check_len = udp_check->reply_len; if (len < check_len) check_len = len; /* Check the received data matches */ for (i = 0; i < check_len; i++) { if ((recv_data[i] ^ udp_check->reply_data[i]) & (udp_check->reply_mask ? ~udp_check->reply_mask[i] : ~0)) return true; } /* Success */ return false; } static void udp_check_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); udp_check_t *udp_check = CHECKER_ARG(checker); int status; uint8_t *recv_buf; size_t len = udp_check->max_reply_len + 1; recv_buf = udp_check->require_reply ? MALLOC(udp_check->max_reply_len + 1) : NULL; status = udp_socket_state(thread->u.f.fd, thread, recv_buf, &len); thread_close_fd(thread); if (status == connect_success) { /* coverity[var_deref_model] - udp_check->reply_data is only set if udp_check->require_reply is set */ if (udp_check->reply_data && check_udp_reply(recv_buf, len, udp_check)) { if (checker->is_up && (global_data->checker_log_all_failures || checker->log_all_failures)) log_message(LOG_INFO, "UDP check to %s reply data mismatch." , FMT_CHK(checker)); udp_epilog(thread, false); } else udp_epilog(thread, true); } else { if (checker->is_up && (global_data->checker_log_all_failures || checker->log_all_failures)) log_message(LOG_INFO, "UDP connection to %s failed." , FMT_CHK(checker)); udp_epilog(thread, false); } if (recv_buf) FREE(recv_buf); return; } static void udp_connect_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); udp_check_t *udp_check = CHECKER_ARG(checker); conn_opts_t *co = checker->co; int fd; int status; /* * Register a new checker thread & return * if checker is disabled */ if (!checker->enabled) { thread_add_timer(thread->master, udp_connect_thread, checker, checker->delay_loop); return; } if ((fd = socket(co->dst.ss_family, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP)) == -1) { log_message(LOG_INFO, "UDP connect fail to create socket. Rescheduling."); thread_add_timer(thread->master, udp_connect_thread, checker, checker->delay_loop); return; } status = udp_bind_connect(fd, co, udp_check->payload, udp_check->payload_len); /* handle udp connection status & register check worker thread */ if (udp_icmp_check_state(fd, status, thread, udp_check_thread, co->connection_to)) { close(fd); udp_epilog(thread, false); } return; } #ifdef THREAD_DUMP void register_check_udp_addresses(void) { register_thread_address("udp_check_thread", udp_check_thread); register_thread_address("udp_connect_thread", udp_connect_thread); } #endif keepalived-2.3.3/keepalived/check/ipwrapper.c0000664000175000017500000010277414661620303014672 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Manipulation functions for IPVS & IPFW wrappers. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include "ipwrapper.h" #include "check_api.h" #include "logger.h" #include "utils.h" #include "main.h" #ifdef _WITH_SNMP_CHECKER_ #include "check_snmp.h" #endif #include "global_data.h" #include "smtp.h" #include "check_daemon.h" #include "track_file.h" #ifdef _WITH_NFTABLES_ #include "check_nftables.h" #include "check_data.h" #endif static bool __attribute((pure)) vs_iseq(const virtual_server_t *vs_a, const virtual_server_t *vs_b) { if (!vs_a->vsgname != !vs_b->vsgname) return false; if (vs_a->vsgname) { /* Should we check the vsg entries match? */ if (inet_sockaddrport(&vs_a->addr) != inet_sockaddrport(&vs_b->addr)) return false; return !strcmp(vs_a->vsgname, vs_b->vsgname); } else if (vs_a->af != vs_b->af) return false; else if (vs_a->vfwmark) { if (vs_a->vfwmark != vs_b->vfwmark) return false; } else { if (vs_a->service_type != vs_b->service_type || !sockstorage_equal(&vs_a->addr, &vs_b->addr)) return false; } return true; } static bool __attribute((pure)) vsge_iseq(const virtual_server_group_entry_t *vsge_a, const virtual_server_group_entry_t *vsge_b) { if (vsge_a->is_fwmark != vsge_b->is_fwmark) return false; if (vsge_a->is_fwmark) return vsge_a->vfwmark == vsge_b->vfwmark; if (!sockstorage_equal(&vsge_a->addr, &vsge_b->addr) || !sockstorage_equal(&vsge_a->addr_end, &vsge_b->addr_end)) return false; return true; } /* Returns the sum of all alive RS weight in a virtual server. */ static unsigned long __attribute__ ((pure)) weigh_live_realservers(virtual_server_t *vs) { real_server_t *rs; long count = 0; list_for_each_entry(rs, &vs->rs, e_list) { if (ISALIVE(rs)) count += real_weight(rs->effective_weight); } return count; } static void notify_fifo_vs(virtual_server_t *vs) { const char *state = vs->quorum_state_up ? "UP" : "DOWN"; size_t size; char *line; const char *vs_str; if (global_data->notify_fifo.fd == -1 && global_data->lvs_notify_fifo.fd == -1) return; vs_str = FMT_VS(vs); size = strlen(vs_str) + strlen(state) + 5; line = MALLOC(size + 1); if (!line) return; snprintf(line, size + 1, "VS %s %s\n", vs_str, state); if (global_data->notify_fifo.fd != -1) if (write(global_data->notify_fifo.fd, line, size) == -1) { /* empty */ } if (global_data->lvs_notify_fifo.fd != -1) if (write(global_data->lvs_notify_fifo.fd, line, size) == -1) { /* empty */ } FREE(line); } static void notify_fifo_rs(virtual_server_t* vs, real_server_t* rs) { const char *state = rs->alive ? "UP" : "DOWN"; size_t size; char *line; const char *rs_str; const char *vs_str; if (global_data->notify_fifo.fd == -1 && global_data->lvs_notify_fifo.fd == -1) return; rs_str = FMT_RS(rs, vs); vs_str = FMT_VS(vs); size = strlen(rs_str) + strlen(vs_str) + strlen(state) + 6; line = MALLOC(size + 1); if (!line) return; snprintf(line, size + 1, "RS %s %s %s\n", rs_str, vs_str, state); if (global_data->notify_fifo.fd != -1) if (write(global_data->notify_fifo.fd, line, size) == - 1) { /* empty */ } if (global_data->lvs_notify_fifo.fd != -1) if (write(global_data->lvs_notify_fifo.fd, line, size) == -1) { /* empty */ } FREE(line); } static void do_vs_notifies(virtual_server_t* vs, bool init, long threshold, long weight_sum, bool stopping) { notify_script_t *notify_script = vs->quorum_state_up ? vs->notify_quorum_up : vs->notify_quorum_down; char message[80]; #ifdef _WITH_SNMP_CHECKER_ check_snmp_quorum_trap(vs, stopping); #endif /* Only send non SNMP notifies when stopping if omega set */ if (stopping && !vs->omega) return; if (notify_script) { if (stopping) system_call_script(master, child_killed_thread, NULL, TIMER_HZ, notify_script); else notify_exec(notify_script); } notify_fifo_vs(vs); if (vs->smtp_alert) { if (stopping) snprintf(message, sizeof(message), "=> Shutting down <="); else snprintf(message, sizeof(message), "=> %s %u+%u=%ld <= %ld <=", vs->quorum_state_up ? init ? "Starting with quorum up" : "Gained quorum" : init ? "Starting with quorum down" : "Lost quorum", vs->quorum, vs->hysteresis, threshold, weight_sum); smtp_alert(SMTP_MSG_VS, vs, vs->quorum_state_up ? "UP" : "DOWN", message); } } static void do_rs_notifies(virtual_server_t* vs, real_server_t* rs, bool stopping) { notify_script_t *notify_script = rs->alive ? rs->notify_up : rs->notify_down; if (notify_script) { if (stopping) system_call_script(master, child_killed_thread, NULL, TIMER_HZ, notify_script); else notify_exec(notify_script); } notify_fifo_rs(vs, rs); /* The sending of smtp_alerts is handled by the individual checker * so that the message can have context for the checker */ #ifdef _WITH_SNMP_CHECKER_ check_snmp_rs_trap(rs, vs, stopping); #endif } /* Remove a realserver IPVS rule */ static void update_vs_notifies(virtual_server_t *vs, bool stopping) { long threshold = vs->quorum - vs->hysteresis; long weight_sum; /* Sooner or later VS will lose the quorum (if any). However, * we don't push in a sorry server then, hence the regression * is intended. */ weight_sum = weigh_live_realservers(vs); if (stopping || (vs->quorum_state_up && (!weight_sum || weight_sum < threshold))) { vs->quorum_state_up = false; do_vs_notifies(vs, false, threshold, weight_sum, stopping); } } static void clear_service_rs(virtual_server_t *vs, real_server_t *rs, bool stopping) { smtp_rs rs_info = { .vs = vs }; bool sav_inhibit; if (rs->set || stopping) log_message(LOG_INFO, "%s %sservice %s from VS %s", stopping ? "Shutting down" : "Removing", rs->inhibit && !rs->alive ? "(inhibited) " : "", FMT_RS(rs, vs), FMT_VS(vs)); if (!rs->set) return; /* Force removal of real servers with inhibit_on_failure set */ sav_inhibit = rs->inhibit; rs->inhibit = false; ipvs_cmd(LVS_CMD_DEL_DEST, vs, rs); rs->inhibit = sav_inhibit; /* Restore inhibit flag */ if (!rs->alive) return; UNSET_ALIVE(rs); /* We always want to send SNMP messages on shutdown */ if (!vs->omega && stopping) { #ifdef _WITH_SNMP_CHECKER_ check_snmp_rs_trap(rs, vs, true); #endif return; } /* In Omega mode we call VS and RS down notifiers * all the way down the exit, as necessary. */ do_rs_notifies(vs, rs, stopping); /* Send SMTP alert */ if (rs->smtp_alert) { rs_info.rs = rs; smtp_alert(SMTP_MSG_RS_SHUT, &rs_info, "DOWN", stopping ? "=> Shutting down <=" : "=> Removing <="); } } static void clear_service_rs_list(virtual_server_t *vs, list_head_t *l, bool stopping) { real_server_t *rs; list_for_each_entry(rs, l, e_list) clear_service_rs(vs, rs, stopping); update_vs_notifies(vs, stopping); } static void clear_vsg_rs_counts(virtual_server_t *vs) { virtual_server_group_entry_t *vsg_entry; real_server_t *rs; list_for_each_entry(vsg_entry, &vs->vsg->addr_range, e_list) { list_for_each_entry(rs, &vs->rs, e_list) unset_vsge_alive(vsg_entry, vs); } list_for_each_entry(vsg_entry, &vs->vsg->vfwmark, e_list) { list_for_each_entry(rs, &vs->rs, e_list) unset_vsge_alive(vsg_entry, vs); } } /* Remove a virtualserver IPVS rule */ static void clear_service_vs(virtual_server_t * vs, bool stopping) { bool sav_inhibit; if (global_data->lvs_flush_on_stop == LVS_NO_FLUSH) { /* Processing real server queue */ if (vs->s_svr && vs->s_svr->set) { if (vs->s_svr_duplicates_rs) vs->s_svr->set = false; else { /* Ensure removed if inhibit_on_failure set */ sav_inhibit = vs->s_svr->inhibit; vs->s_svr->inhibit = false; ipvs_cmd(LVS_CMD_DEL_DEST, vs, vs->s_svr); vs->s_svr->inhibit = sav_inhibit; } UNSET_ALIVE(vs->s_svr); } /* Even if the sorry server was configured, if we are using * inhibit_on_failure, then real servers may be configured. */ clear_service_rs_list(vs, &vs->rs, stopping); } else { update_vs_notifies(vs, stopping); if (global_data->lvs_flush_on_stop == LVS_FLUSH_VS && vs->vsg) clear_vsg_rs_counts(vs); if (vs->s_svr && vs->s_svr->set) UNSET_ALIVE(vs->s_svr); } /* The above will handle Omega case for VS as well. */ #ifdef _WITH_NFTABLES_ if (VS_USES_VSG_AUTO_FWMARK(vs)) clear_vs_fwmark(vs); #endif ipvs_cmd(LVS_CMD_DEL, vs, NULL); UNSET_ALIVE(vs); } /* IPVS cleaner processing */ void clear_services(void) { virtual_server_t *vs; if (!check_data || list_empty(&check_data->vs)) return; if (global_data->lvs_flush_on_stop == LVS_FLUSH_FULL) { ipvs_flush_cmd(); list_for_each_entry(vs, &check_data->vs, e_list) update_vs_notifies(vs, true); } else { list_for_each_entry(vs, &check_data->vs, e_list) { /* Remove the real servers, and clear the vs unless it is * using a VS group and it is not the last vs of the same * protocol or address family using the group. */ clear_service_vs(vs, true); } } #ifdef _WITH_NFTABLES_ if (global_data->ipvs_nf_table_name) nft_ipvs_end(); #endif } /* Set a realserver IPVS rules */ static void init_service_rs(virtual_server_t *vs) { real_server_t *rs; tracked_file_monitor_t *tfm; int64_t new_weight; list_for_each_entry(rs, &vs->rs, e_list) { if (rs->reloaded) { if (rs->effective_weight != rs->peffective_weight) { /* We need to force a change from the previous weight */ new_weight = rs->effective_weight; rs->effective_weight = rs->peffective_weight; update_svr_wgt(new_weight, vs, rs, false); } /* Do not re-add failed RS instantly on reload */ continue; } /* On a reload with a new RS the num_failed_checkers is updated in set_track_file_checkers_down() */ if (!reload) { list_for_each_entry(tfm, &rs->track_files, e_list) { if (tfm->weight) { if ((int64_t)tfm->file->last_status * tfm->weight * (tfm->weight_reverse ? -1 : 1) <= IPVS_WEIGHT_FAULT) rs->num_failed_checkers++; } else if (tfm->file->last_status) rs->num_failed_checkers++; } } /* In alpha mode, be pessimistic (or realistic?) and don't * add real servers into the VS pool unless inhibit_on_failure. * They will get there later upon healthchecks recovery (if ever). */ if ((!rs->num_failed_checkers && !ISALIVE(rs)) || (rs->inhibit && !rs->set)) { ipvs_cmd(LVS_CMD_ADD_DEST, vs, rs); if (!rs->num_failed_checkers) { SET_ALIVE(rs); if (global_data->rs_init_notifies) do_rs_notifies(vs, rs, false); } } } } static void sync_service_vsg_entry(virtual_server_t *vs, const list_head_t *l) { virtual_server_group_entry_t *vsge; list_for_each_entry(vsge, l, e_list) { if (!vsge->reloaded) { if (vsge->is_fwmark) log_message(LOG_INFO, "VS [FWM %u] added into group %s" , vsge->vfwmark , vs->vsgname); else if (!inet_sockaddrcmp(&vsge->addr, &vsge->addr_end)) log_message(LOG_INFO, "VS [%s] added into group %s" , inet_sockaddrtotrio(&vsge->addr, vs->service_type) , vs->vsgname); else log_message(LOG_INFO, "VS [%s-%s] added into group %s" , inet_sockaddrtotrio(&vsge->addr, vs->service_type) , inet_sockaddrtos(&vsge->addr_end) , vs->vsgname); /* add all reloaded and alive/inhibit-set dests * to the newly created vsg item */ ipvs_group_sync_entry(vs, vsge); } } } static void sync_service_vsg(virtual_server_t *vs) { virtual_server_group_t *vsg = vs->vsg; sync_service_vsg_entry(vs, &vsg->addr_range); sync_service_vsg_entry(vs, &vsg->vfwmark); } /* add or remove _alive_ real servers from a virtual server */ static void perform_quorum_state(virtual_server_t *vs, bool add) { real_server_t *rs; log_message(LOG_INFO, "%s the pool for VS %s" , add?"Adding alive servers to":"Removing alive servers from" , FMT_VS(vs)); list_for_each_entry(rs, &vs->rs, e_list) { if (!ISALIVE(rs)) /* We only handle alive servers */ continue; // ??? The following seems unnecessary if (add) rs->alive = false; ipvs_cmd(add?LVS_CMD_ADD_DEST:LVS_CMD_DEL_DEST, vs, rs); rs->alive = true; } } void set_quorum_states(void) { virtual_server_t *vs; list_for_each_entry(vs, &check_data->vs, e_list) { vs->quorum_state_up = (weigh_live_realservers(vs) >= vs->quorum + vs->hysteresis); } } /* set quorum state depending on current weight of real servers */ static void update_quorum_state(virtual_server_t * vs, bool init) { long weight_sum = weigh_live_realservers(vs); long threshold; threshold = vs->quorum + (vs->quorum_state_up ? -1 : 1) * vs->hysteresis; /* If we have just gained quorum, it's time to consider notify_up. */ if (!vs->quorum_state_up && weight_sum >= threshold) { vs->quorum_state_up = true; log_message(LOG_INFO, "Gained quorum %u+%u=%ld <= %ld for VS %s" , vs->quorum , vs->hysteresis , threshold , weight_sum , FMT_VS(vs)); if (vs->s_svr && ISALIVE(vs->s_svr)) { /* Removing sorry server since we don't need it anymore */ log_message(LOG_INFO, "%s sorry server %s from VS %s" , (vs->s_svr->inhibit ? "Disabling" : "Removing") , FMT_RS(vs->s_svr, vs) , FMT_VS(vs)); ipvs_cmd(LVS_CMD_DEL_DEST, vs, vs->s_svr); vs->s_svr->alive = false; /* Adding back alive real servers */ perform_quorum_state(vs, true); } do_vs_notifies(vs, init, threshold, weight_sum, false); return; } else if ((vs->quorum_state_up && (!weight_sum || weight_sum < threshold)) || (init && !vs->quorum_state_up && vs->s_svr && !ISALIVE(vs->s_svr))) { /* We have just lost quorum for the VS, we need to consider * VS notify_down and sorry_server cases * or * We are starting up and need to add the sorry server */ vs->quorum_state_up = false; log_message(LOG_INFO, "%s %u-%u=%ld > %ld for VS %s" , init ? "Starting with quorum down" : "Lost quorum" , vs->quorum , vs->hysteresis , threshold , weight_sum , FMT_VS(vs)); if (vs->s_svr && !ISALIVE(vs->s_svr)) { log_message(LOG_INFO, "%s sorry server %s to VS %s" , (vs->s_svr->inhibit ? "Enabling" : "Adding") , FMT_RS(vs->s_svr, vs) , FMT_VS(vs)); /* Remove remaining alive real servers */ perform_quorum_state(vs, false); /* the sorry server is now up in the pool, we flag it alive */ ipvs_cmd(LVS_CMD_ADD_DEST, vs, vs->s_svr); vs->s_svr->alive = true; } do_vs_notifies(vs, init, threshold, weight_sum, false); } } /* manipulate add/remove rs according to alive state */ static bool perform_svr_state(bool alive, checker_t *checker) { /* * | ISALIVE(rs) | alive | context * | false | false | first check failed under alpha mode, unreachable here * | false | true | RS went up, add it to the pool * | true | false | RS went down, remove it from the pool * | true | true | first check succeeded w/o alpha mode, unreachable here */ virtual_server_t * vs = checker->vs; real_server_t * rs = checker->rs; if (ISALIVE(rs) == alive) return true; log_message(LOG_INFO, "%sing service %s %s VS %s" , alive ? (rs->inhibit) ? "Enabl" : "Add" : (rs->inhibit) ? "Disabl" : "Remov" , FMT_RS(rs, vs) , (rs->inhibit) ? "of" : alive ? "to" : "from" , FMT_VS(vs)); /* Change only if we have quorum or no sorry server */ if (vs->quorum_state_up || !vs->s_svr || !ISALIVE(vs->s_svr)) { if (ipvs_cmd(alive ? LVS_CMD_ADD_DEST : LVS_CMD_DEL_DEST, vs, rs)) return false; } rs->alive = alive; do_rs_notifies(vs, rs, false); /* We may have changed quorum state. If the quorum wasn't up * but is now up, this is where the rs is added. */ update_quorum_state(vs, false); return true; } /* Set a virtualserver IPVS rules */ static bool init_service_vs(virtual_server_t * vs) { /* Init the VS root */ if (!ISALIVE(vs) || vs->vsg) { #ifdef _WITH_NFTABLES_ if (ISALIVE(vs) && VS_USES_VSG_AUTO_FWMARK(vs)) set_vs_fwmark(vs); else #endif { ipvs_cmd(LVS_CMD_ADD, vs, NULL); SET_ALIVE(vs); } } /* Processing real server queue */ init_service_rs(vs); if (vs->reloaded && vs->vsg #ifdef _WITH_NFTABLES_ && !VS_USES_VSG_AUTO_FWMARK(vs) #endif ) { /* add reloaded dests into new vsg entries */ sync_service_vsg(vs); } /* we may have got/lost quorum due to quorum setting changed */ /* also update, in case we need the sorry server in alpha mode */ update_quorum_state(vs, true); /* If we have a sorry server with inhibit, add it now */ if (vs->s_svr && vs->s_svr->inhibit && !vs->s_svr->set) { if (vs->s_svr_duplicates_rs) vs->s_svr->set = true; else { /* Make sure the sorry server is configured with weight 0 */ vs->s_svr->num_failed_checkers = 1; ipvs_cmd(LVS_CMD_ADD_DEST, vs, vs->s_svr); vs->s_svr->num_failed_checkers = 0; } } return true; } /* Set IPVS rules */ bool init_services(void) { virtual_server_t *vs; list_for_each_entry(vs, &check_data->vs, e_list) { if (!init_service_vs(vs)) return false; } return true; } /* Store new weight in real_server struct and then update kernel. */ void update_svr_wgt(int64_t weight, virtual_server_t * vs, real_server_t * rs , bool update_quorum) { int old_weight, new_weight; new_weight = real_weight(weight); old_weight = real_weight(rs->effective_weight); rs->effective_weight = weight; if (new_weight != old_weight) { log_message(LOG_INFO, "Changing weight from %d to %d for %sactive service %s of VS %s" , old_weight , new_weight , ISALIVE(rs) ? "" : "in" , FMT_RS(rs, vs) , FMT_VS(vs)); /* * Have weight change take effect now only if rs is in * the pool and alive and the quorum is met (or if * there is no sorry server). If not, it will take * effect later when it becomes alive. */ if (rs->set && ISALIVE(rs) && (vs->quorum_state_up || !vs->s_svr || !ISALIVE(vs->s_svr))) ipvs_cmd(LVS_CMD_EDIT_DEST, vs, rs); if (update_quorum) update_quorum_state(vs, false); } } void set_checker_state(checker_t *checker, bool up) { if (checker->is_up == up) return; checker->is_up = up; if (!up) checker->rs->num_failed_checkers++; else if (checker->rs->num_failed_checkers) checker->rs->num_failed_checkers--; } /* Update checker's state */ void update_svr_checker_state(bool alive, checker_t *checker) { if (checker->is_up == alive) { if (!checker->has_run) { if (checker->alpha || !alive) do_rs_notifies(checker->vs, checker->rs, false); checker->has_run = true; } return; } checker->has_run = true; if (alive) { /* call the UP handler unless any more failed checks found */ if (checker->rs->num_failed_checkers <= 1) { if (!perform_svr_state(true, checker)) return; } } else { /* Handle not alive state */ if (checker->rs->num_failed_checkers == 0) { if (!perform_svr_state(false, checker)) return; } } set_checker_state(checker, alive); } /* Check if a vsg entry is in new data */ static virtual_server_group_entry_t * __attribute__ ((pure)) vsge_exist(virtual_server_group_entry_t *vsg_entry, list_head_t *l) { virtual_server_group_entry_t *vsge; list_for_each_entry(vsge, l, e_list) { if (vsge_iseq(vsg_entry, vsge)) return vsge; } return NULL; } /* Clear the diff vsge of old group */ static void clear_diff_vsge(list_head_t *old, list_head_t *new, virtual_server_t *old_vs) { virtual_server_group_entry_t *vsge, *new_vsge; list_for_each_entry(vsge, old, e_list) { new_vsge = vsge_exist(vsge, new); if (new_vsge) { new_vsge->reloaded = true; continue; } if (vsge->is_fwmark) log_message(LOG_INFO, "VS [%u] in group %s no longer exists", vsge->vfwmark, old_vs->vsgname); else if (!inet_sockaddrcmp(&vsge->addr, &vsge->addr_end)) log_message(LOG_INFO, "VS [%s] in group %s no longer exists" , inet_sockaddrtotrio(&vsge->addr, old_vs->service_type) , old_vs->vsgname); else log_message(LOG_INFO, "VS [%s-%s] in group %s no longer exists" , inet_sockaddrtotrio(&vsge->addr, old_vs->service_type) , inet_sockaddrtos(&vsge->addr_end) , old_vs->vsgname); ipvs_group_remove_entry(old_vs, vsge); } } static void update_alive_counts_vsge(list_head_t *old, list_head_t *new) { virtual_server_group_entry_t *old_vsge, *new_vsge; list_for_each_entry(old_vsge, old, e_list) { new_vsge = vsge_exist(old_vsge, new); if (!new_vsge) continue; if (old_vsge->is_fwmark) { new_vsge->fwm4_alive = old_vsge->fwm4_alive; new_vsge->fwm6_alive = old_vsge->fwm6_alive; } else { new_vsge->tcp_alive = old_vsge->tcp_alive; new_vsge->udp_alive = old_vsge->udp_alive; new_vsge->sctp_alive = old_vsge->sctp_alive; } } } static void update_alive_counts(virtual_server_t *old, virtual_server_t *new) { if (!old->vsg || !new->vsg) return; update_alive_counts_vsge(&old->vsg->addr_range, &new->vsg->addr_range); update_alive_counts_vsge(&old->vsg->vfwmark, &new->vsg->vfwmark); } #ifdef _WITH_NFTABLES_ static void handle_vsg(int family, virtual_server_t *vs) { bool old_val; real_server_t *rs; if ((family == AF_INET && !vs->vsg->have_ipv4) || (family == AF_INET6 && !vs->vsg->have_ipv6)) remove_fwmark_vs(vs, family); else { add_fwmark_vs(vs, family); /* Now add the RSs */ if (family == AF_INET) { old_val = vs->vsg->have_ipv6; vs->vsg->have_ipv6 = false; } else { old_val = vs->vsg->have_ipv4; vs->vsg->have_ipv4 = false; } list_for_each_entry(rs, &vs->rs, e_list) { if (!rs->num_failed_checkers || rs->inhibit) ipvs_cmd(LVS_CMD_ADD_DEST, vs, rs); } if (family == AF_INET) vs->vsg->have_ipv6 = old_val; else vs->vsg->have_ipv4 = old_val; } } #endif /* Clear the diff vsg of the old vs */ static void clear_diff_vsg(virtual_server_t *old_vs, virtual_server_t *new_vs) { virtual_server_group_t *old = old_vs->vsg; virtual_server_group_t *new = new_vs->vsg; #ifdef _WITH_NFTABLES_ bool vsg_already_done; proto_index_t proto_index; if (VS_USES_VSG_AUTO_FWMARK(old_vs)) { proto_index = protocol_to_index(new_vs->service_type); vsg_already_done = !!new_vs->vsg->auto_fwmark[proto_index]; new_vs->vsg->auto_fwmark[proto_index] = old_vs->vsg->auto_fwmark[proto_index]; if (new_vs->vsg->have_ipv4 != old_vs->vsg->have_ipv4) handle_vsg(AF_INET, new_vs); if (new_vs->vsg->have_ipv6 != old_vs->vsg->have_ipv6) handle_vsg(AF_INET6, new_vs); /* We have already updated this vsg */ if (vsg_already_done) return; } #endif /* Diff the group entries */ clear_diff_vsge(&old->addr_range, &new->addr_range, old_vs); clear_diff_vsge(&old->vfwmark, &new->vfwmark, old_vs); } /* Check if a vs exist in new data and returns pointer to it */ static virtual_server_t* __attribute__ ((pure)) vs_exist(virtual_server_t * old_vs) { virtual_server_t *vs; list_for_each_entry(vs, &check_data->vs, e_list) { if (vs_iseq(old_vs, vs)) return vs; } return NULL; } /* Check if rs is in new vs data */ static real_server_t * __attribute__ ((pure)) rs_exist(real_server_t *old_rs, list_head_t *l) { real_server_t *rs; list_for_each_entry(rs, l, e_list) { if (rs_iseq(rs, old_rs)) return rs; } return NULL; } static void migrate_checkers(virtual_server_t *vs, real_server_t *old_rs, real_server_t *new_rs) { checker_t *old_c, *new_c; checker_t dummy_checker; bool a_checker_has_run = false; if (!list_empty(&old_rs->checkers_list)) { list_for_each_entry(new_c, &new_rs->checkers_list, rs_list) { if (!new_c->checker_funcs->compare) continue; list_for_each_entry(old_c, &old_rs->checkers_list, rs_list) { if (old_c->checker_funcs->type == new_c->checker_funcs->type && new_c->checker_funcs->compare(old_c, new_c)) { /* Update status if different */ if (old_c->has_run && old_c->is_up != new_c->is_up) set_checker_state(new_c, old_c->is_up); /* Transfer some other state flags */ new_c->has_run = old_c->has_run; /* If we have already had sufficient retries for the new retry value, * we hadn't already failed, so just require one more failure to trigger * failed state. * If we no longer have any retries, one more failure should trigger * failed state. */ if (!new_c->is_up) new_c->retry_it = new_c->retry + 1; else if (old_c->retry_it >= new_c->retry) new_c->retry_it = new_c->retry; else new_c->retry_it = old_c->retry_it; if (new_c->checker_funcs->migrate) new_c->checker_funcs->migrate(new_c, old_c); break; } } } } /* Find out how many checkers are really failed */ new_rs->num_failed_checkers = 0; list_for_each_entry(new_c, &new_rs->checkers_list, rs_list) { if (new_c->has_run && !new_c->is_up) new_rs->num_failed_checkers++; if (new_c->has_run) a_checker_has_run = true; } /* If a checker has failed, set new alpha checkers to be down until * they have run. */ if (new_rs->num_failed_checkers || (!new_rs->alive && !a_checker_has_run)) { list_for_each_entry(new_c, &new_rs->checkers_list, rs_list) { if (!new_c->has_run) { if (new_c->alpha) set_checker_state(new_c, false); /* One failure is enough */ new_c->retry_it = new_c->retry; } } } /* If there are no failed checkers, the RS needs to be up */ if (!new_rs->num_failed_checkers && !new_rs->alive) { dummy_checker.vs = vs; dummy_checker.rs = new_rs; perform_svr_state(true, &dummy_checker); } else if (new_rs->num_failed_checkers && new_rs->set != new_rs->inhibit) { /* ipvs_cmd() checks for alive rather than set */ new_rs->alive = new_rs->set; ipvs_cmd(new_rs->inhibit ? IP_VS_SO_SET_ADDDEST : IP_VS_SO_SET_DELDEST, vs, new_rs); new_rs->alive = false; } } /* Clear the diff rs of the old vs */ static void clear_diff_rs(virtual_server_t *old_vs, virtual_server_t *new_vs) { real_server_t *rs, *new_rs; /* If old vs didn't own rs then nothing return */ if (list_empty(&old_vs->rs)) return; /* remove RS from old vs which are not found in new vs */ list_for_each_entry(rs, &old_vs->rs, e_list) { new_rs = rs_exist(rs, &new_vs->rs); if (!new_rs) { log_message(LOG_INFO, "service %s no longer exist" , FMT_RS(rs, old_vs)); clear_service_rs(old_vs, rs, false); continue; } /* * We reflect the previous alive * flag value to not try to set * already set IPVS rule. */ new_rs->alive = rs->alive; new_rs->set = rs->set; new_rs->effective_weight = rs->effective_weight; new_rs->peffective_weight = rs->effective_weight; new_rs->reloaded = true; /* * We must migrate the state of the old checkers. * If we do not, the new RS is in a state where it’s reported * as down with no check failed. As a result, the server will never * be put back up when it’s alive again in check_tcp.c#83 because * of the check that put a rs up only if it was not previously up. * For alpha mode checkers, if it was up, we don't need another * success to say it is now up. */ migrate_checkers(new_vs, rs, new_rs); /* Do we need to update the RS configuration? */ if ((new_rs->alive && new_rs->effective_weight != rs->effective_weight) || #ifdef _HAVE_IPVS_TUN_TYPE_ rs->tun_type != new_rs->tun_type || rs->tun_port != new_rs->tun_port || #ifdef _HAVE_IPVS_TUN_CSUM_ rs->tun_flags != new_rs->tun_flags || #endif #endif rs->forwarding_method != new_rs->forwarding_method) ipvs_cmd(LVS_CMD_EDIT_DEST, new_vs, new_rs); } update_vs_notifies(old_vs, false); } /* clear sorry server, but only if changed */ static void clear_diff_s_srv(virtual_server_t *old_vs, virtual_server_t *new_vs) { real_server_t *old_ss = old_vs->s_svr; real_server_t *new_ss = new_vs->s_svr; bool reinstate_alive_rs; if (!old_ss) return; if (new_ss && rs_iseq(old_ss, new_ss)) { /* which fields are really used on s_svr? */ new_ss->alive = old_ss->alive; new_ss->set = old_ss->set; new_ss->effective_weight = new_ss->iweight; new_ss->reloaded = true; if (old_ss->inhibit == new_ss->inhibit || old_ss->alive) return; } /* With no sorry server configured, any alive real servers * need to be reinstated. */ reinstate_alive_rs = old_ss->alive && !new_ss; if (old_ss->inhibit && !ISALIVE(old_ss)) { /* Force removing the old SS */ SET_ALIVE(old_ss); old_ss->inhibit = false; } if (ISALIVE(old_ss)) { log_message(LOG_INFO, "Removing sorry server %s from VS %s" , FMT_RS(old_ss, old_vs) , FMT_VS(old_vs)); ipvs_cmd(LVS_CMD_DEL_DEST, old_vs, old_ss); new_ss->set = false; if (reinstate_alive_rs) perform_quorum_state(new_vs, true); } } /* When reloading configuration, remove negative diff entries * and copy status of existing entries to the new ones */ void clear_diff_services(void) { virtual_server_t *vs, *new_vs; /* Remove diff entries from previous IPVS rules */ list_for_each_entry(vs, &old_check_data->vs, e_list) { /* * Try to find this vs in the new conf data * reloaded. */ new_vs = vs_exist(vs); if (!new_vs) { if (vs->vsgname) log_message(LOG_INFO, "Removing Virtual Server Group [%s]", vs->vsgname); else log_message(LOG_INFO, "Removing Virtual Server %s", FMT_VS(vs)); /* Clear VS entry */ clear_service_vs(vs, false); continue; } /* copy status fields from old VS */ new_vs->alive = vs->alive; new_vs->quorum_state_up = vs->quorum_state_up; new_vs->reloaded = true; if (using_ha_suspend) new_vs->ha_suspend_addr_count = vs->ha_suspend_addr_count; if (vs->vsgname) clear_diff_vsg(vs, new_vs); /* If vs exist, perform rs pool diff */ /* omega = false must not prevent the notifiers from being called, because the VS still exists in new configuration */ if (strcmp(vs->sched, new_vs->sched) || vs->flags != new_vs->flags || strcmp(vs->pe_name, new_vs->pe_name) || vs->persistence_granularity != new_vs->persistence_granularity || vs->persistence_timeout != new_vs->persistence_timeout) ipvs_cmd(IP_VS_SO_SET_EDIT, new_vs, NULL); vs->omega = true; clear_diff_rs(vs, new_vs); clear_diff_s_srv(vs, new_vs); update_alive_counts(vs, new_vs); } } /* This is only called during a reload. Any new real server with * alpha mode checkers should start in down state */ void check_new_rs_state(void) { virtual_server_t *vs; real_server_t *rs; checker_t *checker; list_for_each_entry(vs, &check_data->vs, e_list) { list_for_each_entry(rs, &vs->rs, e_list) { list_for_each_entry(checker, &rs->checkers_list, rs_list) { if (checker->rs->reloaded) continue; if (!checker->alpha) continue; set_checker_state(checker, false); UNSET_ALIVE(checker->rs); } } } } void link_vsg_to_vs(void) { virtual_server_t *vs, *vs_tmp; virtual_server_group_t *vsg; unsigned vsg_member_no; int vsg_af; if (list_empty(&check_data->vs)) return; list_for_each_entry_safe(vs, vs_tmp, &check_data->vs, e_list) { if (!vs->vsgname) continue; vs->vsg = ipvs_get_group_by_name(vs->vsgname, &check_data->vs_group); if (!vs->vsg) { log_message(LOG_INFO, "Virtual server group %s specified but not configured" " - ignoring virtual server %s" , vs->vsgname, FMT_VS(vs)); free_vs(vs); continue; } /* Check the vs and vsg address families match */ if (vs->vsg->have_ipv4 == vs->vsg->have_ipv6) vsg_af = AF_UNSPEC; else if (vs->vsg->have_ipv4) vsg_af = AF_INET; else vsg_af = AF_INET6; /* We can have mixed IPv4 and IPv6 in a vsg only if all fwmarks have a family, * and also all the real/sorry servers of the virtual server are tunnelled. */ if (vs->vsg->have_ipv4 && vs->vsg->have_ipv6 && vs->af != AF_UNSPEC) { log_message(LOG_INFO, "%s: virtual server group with IPv4 & IPv6 doesn't" " match virtual server %s - ignoring" , vs->vsgname, FMT_VS(vs)); free_vs(vs); } else if ((vs->vsg->have_ipv4 && vs->af == AF_INET6) || (vs->vsg->have_ipv6 && vs->af == AF_INET)) { log_message(LOG_INFO, "%s: address family doesn't match" " virtual server %s - ignoring" , vs->vsgname, FMT_VS(vs)); free_vs(vs); } else if (vsg_af != AF_UNSPEC) { if (vs->af == AF_UNSPEC) vs->af = vsg_af; else if (vsg_af != vs->af) { log_message(LOG_INFO, "%s: address family doesn't" " match virtual server %s - ignoring" , vs->vsgname, FMT_VS(vs)); free_vs(vs); } } else if (vs->af == AF_UNSPEC && vs->vsg && vs->vsg->fwmark_no_family) { log_message(LOG_INFO, "%s: Virtual server %s address family cannot be determined," " defaulting to IPv4" , vs->vsgname, FMT_VS(vs)); } } /* The virtual server port number is used to identify the sequence number of the virtual server in the group */ list_for_each_entry(vsg, &check_data->vs_group, e_list) { vsg_member_no = 0; list_for_each_entry(vs, &check_data->vs, e_list) { if (!vs->vsgname) continue; if (!strcmp(vs->vsgname, vsg->gname)) { /* We use the IPv4 port since there is no address family */ PTR_CAST(struct sockaddr_in, &vs->addr)->sin_port = htons(vsg_member_no); vsg_member_no++; } } } } keepalived-2.3.3/keepalived/check/check_nftables.c0000664000175000017500000004033314200525725015605 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: check_nftables.c * * Author: Quentin Armitage, * * 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) 2020-2020 Alexandre Cassen, */ /* Up to commit 0ec6c01f this used libnftnl/libmnl, but that had overheads, * and constructing the netlink packets directly works just as well. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_NFTNL_UDATA #include #endif #include #include #include #include #include #include #ifdef NEED_FAVOR_BSD #define __FAVOR_BSD #endif #include #include #include #include #include #include #include "logger.h" #include "global_data.h" #include "list_head.h" #include "utils.h" #include "nftables.h" #include "check_nftables.h" struct sctphdr { __be16 sh_sport; __be16 sh_dport; __be32 sh_vtag; __be32 sh_checksum; }; static bool ipvs_setup[2]; static bool ipvs_tcp_setup[2]; static bool ipvs_udp_setup[2]; static bool ipvs_sctp_setup[2]; static unsigned next_fwmark; // Copy of nft_setup_ipv4() static void nft_ipvs_setup(struct mnl_nlmsg_batch *batch, int af) { struct nlmsghdr *nlh; struct nftnl_table *ta; struct nftnl_chain *t; int nfproto = af == AF_INET ? NFPROTO_IPV4 : NFPROTO_IPV6; /* nft add table ip keepalived */ ta = table_add_parse(NFPROTO_IPV4, global_data->ipvs_nf_table_name); nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWTABLE, nfproto, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, ta); nftnl_table_free(ta); my_mnl_nlmsg_batch_next(batch); /* nft add chain ip keepalived_ipvs in { type filter hook input priority -1; policy accept } */ t = chain_add_parse(global_data->ipvs_nf_table_name, "in"); if (t == NULL) exit(EXIT_FAILURE); nlh = nftnl_chain_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWCHAIN, nfproto, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_chain_set_u32(t, NFTNL_CHAIN_HOOKNUM, NF_INET_LOCAL_IN); nftnl_chain_set_str(t, NFTNL_CHAIN_TYPE, "filter"); nftnl_chain_set_s32(t, NFTNL_CHAIN_PRIO, global_data->ipvs_nf_chain_priority); nftnl_chain_set_u32(t, NFTNL_CHAIN_POLICY, NF_ACCEPT); nftnl_chain_nlmsg_build_payload(nlh, t); nftnl_chain_free(t); my_mnl_nlmsg_batch_next(batch); ipvs_setup[af == AF_INET6] = true; } static const char * ipvs_map_name(uint16_t protocol) { return protocol == IPPROTO_TCP ? "tcp_map" : protocol == IPPROTO_UDP ? "udp_map" : "sctp_map"; } // Copied from setup_rule_move_igmp static struct nftnl_rule * setup_rule_set_mark(uint8_t family, const char *table, const char *chain, const char *handle, uint8_t l4_protocol, const char *set_map) { /* nft add rule ip keepalived_ipvs in tcp meta l4proto tcp meta mark set ip daddr . tcp dport map @set_fwmark_tcp return */ struct nftnl_rule *r = NULL; uint64_t handle_num; r = nftnl_rule_alloc(); if (r == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_rule_set_str(r, NFTNL_RULE_TABLE, table); nftnl_rule_set_str(r, NFTNL_RULE_CHAIN, chain); nftnl_rule_set_u32(r, NFTNL_RULE_FAMILY, family); if (handle != NULL) { handle_num = atoll(handle); nftnl_rule_set_u64(r, NFTNL_RULE_POSITION, handle_num); } #if HAVE_DECL_NFT_META_L4PROTO add_meta(r, NFT_META_L4PROTO, NFT_REG_1); /* From Linux 3.14 */ #else if (family == NFPROTO_IPV4) add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct iphdr, protocol), sizeof(((struct iphdr *)NULL)->protocol)); else add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct ip6_hdr, ip6_nxt), sizeof(((struct ip6_hdr *)NULL)->ip6_nxt)); #endif add_cmp(r, NFT_REG_1, NFT_CMP_EQ, &l4_protocol, sizeof(l4_protocol)); if (family == NFPROTO_IPV4) add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct iphdr, daddr), sizeof(((struct iphdr *)NULL)->daddr)); else add_payload(r, NFT_PAYLOAD_NETWORK_HEADER, NFT_REG_1, offsetof(struct ip6_hdr, ip6_dst), sizeof(((struct ip6_hdr *)NULL)->ip6_dst)); if (l4_protocol == IPPROTO_TCP) add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, family == NFPROTO_IPV4 ? 9 : 2, offsetof(struct tcphdr, th_dport), sizeof(((struct tcphdr*)NULL)->th_dport)); else if (l4_protocol == IPPROTO_UDP) add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, family == NFPROTO_IPV4 ? 9 : 2, offsetof(struct udphdr, uh_dport), sizeof(((struct udphdr*)NULL)->uh_dport)); else if (l4_protocol == IPPROTO_SCTP) /* Without this check gcc warns about identical branches */ add_payload(r, NFT_PAYLOAD_TRANSPORT_HEADER, family == NFPROTO_IPV4 ? 9 : 2, offsetof(struct sctphdr, sh_dport), sizeof(((struct sctphdr*)NULL)->sh_dport)); add_lookup(r, NFT_REG_1, NFT_REG_1, set_map, 1, false); add_meta_sreg(r, NFT_META_MARK, NFT_REG_1); add_counter(r); add_immediate_verdict(r, NFT_RETURN, NULL); return r; } static void nft_ipvs_add_set_rule(struct mnl_nlmsg_batch *batch, int af, uint16_t l4_protocol, struct nftnl_set **s) { const char *map_name = ipvs_map_name(l4_protocol); int nfproto = af == AF_INET ? NFPROTO_IPV4 : NFPROTO_IPV6; struct nftnl_rule *r; struct nlmsghdr *nlh; int set_flags = NFT_SET_MAP; #ifdef NFT_RANGE_CONCATS uint8_t field_len[2]; #endif #if HAVE_DECL_NFTNL_SET_EXPR struct nftnl_expr *nle = NULL; #endif if (!ipvs_setup[af == AF_INET6]) nft_ipvs_setup(batch, af); #ifdef NFT_RANGE_CONCATS set_flags |= NFT_SET_CONCAT | NFT_SET_INTERVAL; #endif *s = setup_set(af == AF_INET ? NFPROTO_IPV4 : NFPROTO_IPV6, global_data->ipvs_nf_table_name, map_name, (af == AF_INET ? NFT_TYPE_IPADDR : NFT_TYPE_IP6ADDR ) << NFT_TYPE_BITS | NFT_TYPE_INET_SERVICE, set_flags, NFT_TYPE_MARK); #ifdef NFT_RANGE_CONCATS field_len[0] = af == AF_INET6 ? sizeof(struct in6_addr) : sizeof(struct in_addr); field_len[1] = sizeof(((struct tcphdr*)NULL)->th_dport); nftnl_set_set_data(*s, NFTNL_SET_DESC_CONCAT, field_len, sizeof(field_len)); #endif #if HAVE_DECL_NFTNL_SET_EXPR /* From nft 0.9.5 can add "counter" to set definition */ if (global_data->nf_counters) { nle = nftnl_expr_alloc("counter"); nftnl_set_set_data(*s, NFTNL_SET_EXPR, nle, 0); } #endif nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWSET, nfproto, NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_set_nlmsg_build_payload(nlh, *s); my_mnl_nlmsg_batch_next(batch); r = setup_rule_set_mark(nfproto, global_data->ipvs_nf_table_name, "in", NULL, l4_protocol, map_name); nlh = nftnl_rule_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_NEWRULE, nftnl_rule_get_u32(r, NFTNL_RULE_FAMILY), NLM_F_APPEND|NLM_F_CREATE|NLM_F_ACK, seq++); nftnl_rule_nlmsg_build_payload(nlh, r); nftnl_rule_free(r); my_mnl_nlmsg_batch_next(batch); if (l4_protocol == IPPROTO_TCP) ipvs_tcp_setup[af == AF_INET6] = true; else if (l4_protocol == IPPROTO_UDP) ipvs_udp_setup[af == AF_INET6] = true; else if (l4_protocol == IPPROTO_SCTP) ipvs_sctp_setup[af == AF_INET6] = true; } // Copied from nft_update_vmac_element static void nft_update_ipvs_element(struct mnl_nlmsg_batch *batch, struct nftnl_set *s, const sockaddr_t *addr, #ifdef NFT_RANGE_CONCATS const sockaddr_t *addr_end, #endif uint32_t fwmark, int cmd) { struct nlmsghdr *nlh; struct nftnl_set_elem *e; uint16_t type = cmd == NFT_MSG_NEWSETELEM ? NLM_F_CREATE | NLM_F_ACK : NLM_F_ACK; char buf[sizeof(struct in6_addr) + sizeof(uint32_t)]; unsigned len = 0; union { const struct sockaddr_in *in; const struct sockaddr_in6 *in6; const sockaddr_t *ss; } ss = { .ss = addr }; int nfproto = addr->ss_family == AF_INET ? NFPROTO_IPV4 : NFPROTO_IPV6; /* nft add element ip keepalived in { addr . port : mark } */ nlh = nftnl_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), cmd, nfproto, type, seq++); e = nftnl_set_elem_alloc(); if (nfproto == NFPROTO_IPV4) { memcpy(buf, &ss.in->sin_addr, len = sizeof(ss.in->sin_addr)); memcpy(buf + len, &ss.in->sin_port, sizeof(ss.in->sin_port)); len += sizeof(ss.in->sin_port); } else { memcpy(buf, &ss.in6->sin6_addr, len = sizeof(ss.in6->sin6_addr)); memcpy(buf + len, &ss.in6->sin6_port, sizeof(ss.in6->sin6_port)); len += sizeof(ss.in6->sin6_port); } if (NLMSG_ALIGN(len) > len) memset(buf + len, 0, NLMSG_ALIGN(len) - len); len = NLMSG_ALIGN(len); nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY, buf, len); #ifdef NFT_RANGE_CONCATS ss.ss = addr_end; if (nfproto == NFPROTO_IPV4) { memcpy(buf, &ss.in->sin_addr, len = sizeof(ss.in->sin_addr)); memcpy(buf + len, &ss.in->sin_port, sizeof(ss.in->sin_port)); len += sizeof(ss.in->sin_port); } else { memcpy(buf, &ss.in6->sin6_addr, len = sizeof(ss.in6->sin6_addr)); memcpy(buf + len, &ss.in6->sin6_port, sizeof(ss.in6->sin6_port)); len += sizeof(ss.in6->sin6_port); } if (NLMSG_ALIGN(len) > len) memset(buf + len, 0, NLMSG_ALIGN(len) - len); len = NLMSG_ALIGN(len); nftnl_set_elem_set(e, NFTNL_SET_ELEM_KEY_END, buf, len); #endif nftnl_set_elem_set(e, NFTNL_SET_ELEM_DATA, &fwmark, sizeof(fwmark)); nftnl_set_elem_add(s, e); nftnl_set_elems_nlmsg_build_payload(nlh, s); my_mnl_nlmsg_batch_next(batch); } static void nft_update_ipvs_entry(const sockaddr_t *addr, #ifdef NFT_RANGE_CONCATS const sockaddr_t *addr_end, #endif uint16_t l4_protocol, uint32_t fwmark, int cmd) { struct nftnl_set *s; int setup_index = addr->ss_family == AF_INET6; struct mnl_nlmsg_batch *batch; batch = nft_start_batch(); if (cmd == NFT_MSG_NEWSETELEM && ((l4_protocol == IPPROTO_TCP && !ipvs_tcp_setup[setup_index]) || (l4_protocol == IPPROTO_UDP && !ipvs_udp_setup[setup_index]) || (l4_protocol == IPPROTO_SCTP && !ipvs_sctp_setup[setup_index]))) nft_ipvs_add_set_rule(batch, addr->ss_family, l4_protocol, &s); else { s = nftnl_set_alloc(); if (s == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return; } nftnl_set_set_str(s, NFTNL_SET_TABLE, global_data->ipvs_nf_table_name); nftnl_set_set_str(s, NFTNL_SET_NAME, ipvs_map_name(l4_protocol)); } #ifdef NFT_RANGE_CONCATS nft_update_ipvs_element(batch, s, addr, addr_end, fwmark, cmd); #else nft_update_ipvs_element(batch, s, addr, fwmark, cmd); #endif nftnl_set_free(s); nft_end_batch(batch, false); } #ifdef _INCLUDE_UNUSED_CODE_ void nft_add_ipvs_entry(const sockaddr_t *addr, uint16_t l4_protocol, uint32_t fwmark) { nft_update_ipvs_entry(addr, l4_protocol, fwmark, NFT_MSG_NEWSETELEM); } void nft_remove_ipvs_entry(const sockaddr_t *addr, uint16_t l4_protocol, uint32_t fwmark) { nft_update_ipvs_entry(addr, l4_protocol, fwmark, NFT_MSG_DELSETELEM); } #endif // copy of nft_cleanup() static void nft_ipvs_cleanup(void) { /* ---------------- ------------------ | 0000000020 | | message length | | 00016 | R--- | | type | flags | | 0000000003 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 00 00 0a 00 | | extra header | ---------------- ------------------ ---------------- ------------------ | 0000000036 | | message length | | 02562 | R-A- | | type | flags | NFT_MSG_DELTABLE | 0000000004 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 02 00 00 00 | | extra header | |00015|--|00001| |len |flags| type| | 6b 65 65 70 | | data | k e e p | 61 6c 69 76 | | data | a l i v | 65 64 00 00 | | data | e d ---------------- ------------------ ---------------- ------------------ | 0000000020 | | message length | | 00017 | R--- | | type | flags | | 0000000005 | | sequence number| | 0000000000 | | port ID | ---------------- ------------------ | 00 00 0a 00 | | extra header | ---------------- ------------------ */ struct nftnl_table *t; struct nlmsghdr *nlh; struct mnl_nlmsg_batch *batch; if (!ipvs_setup[0] && !ipvs_setup[1]) return; batch = nft_start_batch(); /* nft delete table ip keepalived */ t = nftnl_table_alloc(); nftnl_table_set_str(t, NFTNL_TABLE_NAME, global_data->ipvs_nf_table_name); if (ipvs_setup[0]) { nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV4, NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, t); my_mnl_nlmsg_batch_next(batch); ipvs_setup[0] = false; ipvs_tcp_setup[0] = false; ipvs_udp_setup[0] = false; ipvs_sctp_setup[0] = false; } /* nft delete table ip6 keepalived */ if (ipvs_setup[1]) { nlh = nftnl_table_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), NFT_MSG_DELTABLE, NFPROTO_IPV6, NLM_F_ACK, seq++); nftnl_table_nlmsg_build_payload(nlh, t); my_mnl_nlmsg_batch_next(batch); ipvs_setup[1] = false; ipvs_tcp_setup[1] = false; ipvs_udp_setup[1] = false; ipvs_sctp_setup[1] = false; } nftnl_table_free(t); nft_end_batch(batch, false); } // Copy of nft_end() void nft_ipvs_end(void) { if (!nl) return; nft_ipvs_cleanup(); mnl_socket_close(nl); nl = NULL; } static inline unsigned get_next_fwmark(void) { if (!next_fwmark) next_fwmark = global_data->ipvs_nftables_start_fwmark; return next_fwmark++; } static void process_fwmark_vsge_range(const sockaddr_t *addr, const sockaddr_t *addr_end, uint16_t service_type, unsigned fwmark, int cmd) { #ifndef NFT_RANGE_CONCATS sockaddr_t sockaddr; struct sockaddr_in *sockaddr4 = PTR_CAST(struct sockaddr_in, &sockaddr); struct sockaddr_in6 *sockaddr6 = PTR_CAST(struct sockaddr_in6, &sockaddr); uint32_t end_addr = 0; /* Stop GCC uninitialised warning */ int i; #endif #ifdef NFT_RANGE_CONCATS nft_update_ipvs_entry(addr, addr_end, service_type, fwmark, cmd); #else sockaddr = *addr; if (addr->ss_family == AF_INET) end_addr = PTR_CAST_CONST(struct sockaddr_in, addr_end)->sin_addr.s_addr; else sockaddr6->sin6_family = AF_INET6; do { nft_update_ipvs_entry(&sockaddr, service_type, fwmark, cmd); if (addr->ss_family == AF_INET) { if (sockaddr4->sin_addr.s_addr == end_addr) break; sockaddr4->sin_addr.s_addr += htonl(1); } else { if (!inet_sockaddrcmp(&sockaddr, addr_end)) break; for (i = 7; i >= 0; i--) { if ((sockaddr6->sin6_addr.s6_addr16[i] = htons(ntohs(sockaddr6->sin6_addr.s6_addr16[i]) + 1))) break; } } } while (true); #endif } static void do_vs_fwmark(virtual_server_t *vs, unsigned fwmark, int cmd) { virtual_server_group_t *vsg = vs->vsg; virtual_server_group_entry_t *vsg_entry; list_for_each_entry(vsg_entry, &vsg->addr_range, e_list) { /* Process the range */ if (cmd == NFT_MSG_DELSETELEM || !vsg_entry->reloaded) process_fwmark_vsge_range(&vsg_entry->addr, &vsg_entry->addr_end, vs->service_type, fwmark, cmd); } } unsigned set_vs_fwmark(virtual_server_t *vs) { proto_index_t proto_index = protocol_to_index(vs->service_type); unsigned fwmark = vs->vsg->auto_fwmark[proto_index] ? vs->vsg->auto_fwmark[proto_index] : get_next_fwmark(); do_vs_fwmark(vs, fwmark, NFT_MSG_NEWSETELEM); return fwmark; } void clear_vs_fwmark(virtual_server_t *vs) { do_vs_fwmark(vs, vs->vsg->auto_fwmark[protocol_to_index(vs->service_type)], NFT_MSG_DELSETELEM); } void remove_vs_fwmark_entry(virtual_server_t *vs, virtual_server_group_entry_t *vsge) { process_fwmark_vsge_range(&vsge->addr, &vsge->addr_end, vs->service_type, vs->vsg->auto_fwmark[protocol_to_index(vs->service_type)], NFT_MSG_DELSETELEM); } keepalived-2.3.3/keepalived/check/ipvswrapper.c0000664000175000017500000007704714640504250015246 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: IPVS Kernel wrapper. Use setsockopt call to add/remove * server to/from the loadbalanced server pool. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "ipvswrapper.h" #include "global_data.h" #include "utils.h" #include "logger.h" #include "libipvs.h" #include "main.h" #include "namespaces.h" #ifdef _WITH_NFTABLES_ #include "check_nftables.h" #include "check_data.h" #endif static bool no_ipvs = false; static const char * __attribute__((pure)) ipvs_cmd_str(int cmd) { switch (cmd) { switch_define_str(IP_VS_SO_SET_ADD); switch_define_str(IP_VS_SO_SET_ADDDEST); switch_define_str(IP_VS_SO_SET_DEL); switch_define_str(IP_VS_SO_SET_DELDEST); switch_define_str(IP_VS_SO_SET_EDIT); switch_define_str(IP_VS_SO_SET_EDITDEST); switch_define_str(IP_VS_SO_SET_FLUSH); switch_define_str(IP_VS_SO_SET_STARTDAEMON); switch_define_str(IP_VS_SO_SET_STOPDAEMON); switch_define_str(IP_VS_SO_SET_TIMEOUT); switch_define_str(IP_VS_SO_SET_ZERO); } return "(unknown)"; } /* fetch virtual server group from group name */ virtual_server_group_t * __attribute__ ((pure)) ipvs_get_group_by_name(const char *gname, list_head_t *l) { virtual_server_group_t *vsg; list_for_each_entry(vsg, l, e_list) { if (!strcmp(vsg->gname, gname)) return vsg; } return NULL; } /* Initialization helpers */ int ipvs_start(void) { log_message(LOG_DEBUG, "%snitializing ipvs", reload ? "Rei" : "I"); /* Initialize IPVS module */ if (ipvs_init(false)) { if (!keepalived_modprobe("ip_vs") || ipvs_init(true)) { log_message(LOG_INFO, "IPVS: Can't initialize ipvs: %s", ipvs_strerror(errno)); no_ipvs = true; return IPVS_ERROR; } } return IPVS_SUCCESS; } void ipvs_stop(void) { if (no_ipvs) return; /* Restore any timeout values we updated */ /* coverity[check_return] - we can't do anything if this fails */ ipvs_set_timeout(NULL); ipvs_close(); } void ipvs_set_timeouts(const ipvs_timeout_t *timeouts) { if (timeouts && !timeouts->tcp_timeout && !timeouts->tcp_fin_timeout && !timeouts->udp_timeout) return; if (ipvs_set_timeout(timeouts)) log_message(LOG_INFO, "Failed to set ipvs timeouts"); } static size_t format_srule(char *buf, const ipvs_service_t *srule) { char *bufp = buf; if (srule->user.fwmark) return sprintf(buf, "Fwm %" PRIu32 "%s", srule->user.fwmark, srule->af == AF_INET6 ? " inet6" : ""); inet_ntop(srule->af, srule->af == AF_INET ? (const void *)&srule->nf_addr.ip : (const void *)&srule->nf_addr.in6, bufp, INET6_ADDRSTRLEN); bufp += strlen(bufp); *bufp++ = ':'; if (srule->user.protocol == IPPROTO_TCP) strcpy(bufp, "tcp"); else if (srule->user.protocol == IPPROTO_UDP) strcpy(bufp, "udp"); else if (srule->user.protocol == IPPROTO_SCTP) strcpy(bufp, "sctp"); else sprintf(bufp, "%d", srule->user.protocol); bufp += strlen(bufp); bufp += sprintf(bufp, ":%d", ntohs(srule->user.port)); return (bufp - buf); } static size_t format_drule(char *buf, const ipvs_dest_t *drule) { char *bufp = buf; *bufp++ = ' '; *bufp++ = '-'; *bufp++ = '>'; *bufp++ = ' '; inet_ntop(drule->af, drule->af == AF_INET ? (const void *)&drule->nf_addr.ip : (const void *)&drule->nf_addr.in6, bufp, INET6_ADDRSTRLEN); bufp += strlen(bufp); bufp += sprintf(bufp, ":%d", ntohs(drule->user.port)); return (bufp - buf); } /* Send user rules to IPVS module */ static int ipvs_talk(int cmd, ipvs_service_t *srule, ipvs_dest_t *drule, ipvs_daemon_t *daemonrule, bool ignore_error) { int result = -1; if (no_ipvs) return result; switch (cmd) { case IP_VS_SO_SET_STARTDAEMON: result = ipvs_start_daemon(daemonrule); break; case IP_VS_SO_SET_STOPDAEMON: result = ipvs_stop_daemon(daemonrule); break; case IP_VS_SO_SET_FLUSH: result = ipvs_flush(); break; case IP_VS_SO_SET_ADD: result = ipvs_add_service(srule); break; case IP_VS_SO_SET_DEL: result = ipvs_del_service(srule); break; case IP_VS_SO_SET_EDIT: result = ipvs_update_service(srule); break; #ifdef _INCLUDE_UNUSED_CODE_ case IP_VS_SO_SET_ZERO: result = ipvs_zero_service(srule); break; #endif case IP_VS_SO_SET_ADDDEST: result = ipvs_add_dest(srule, drule); break; case IP_VS_SO_SET_DELDEST: result = ipvs_del_dest(srule, drule); break; case IP_VS_SO_SET_EDITDEST: if ((result = ipvs_update_dest(srule, drule)) && (errno == ENOENT)) { cmd = IP_VS_SO_SET_ADDDEST; result = ipvs_add_dest(srule, drule); } break; default: log_message(LOG_INFO, "ipvs_talk() called with unknown command %d", cmd); } if (ignore_error) result = 0; else if (result) { char buf[2 + INET6_ADDRSTRLEN + 6 + 5 + 4 + INET6_ADDRSTRLEN + 1 + 5 + 1 + 1]; /* " (" + IPv6 + ":sctp:" + port + " -> " + IPV6 + ":" + port + ")" */ if (errno == EEXIST && (cmd == IP_VS_SO_SET_ADD || cmd == IP_VS_SO_SET_ADDDEST)) result = 0; else if (errno == ENOENT && (cmd == IP_VS_SO_SET_DEL || cmd == IP_VS_SO_SET_DELDEST)) result = 0; buf[0] = ' '; buf[1] = '('; if (cmd == IP_VS_SO_SET_ADD || cmd == IP_VS_SO_SET_DEL || cmd == IP_VS_SO_SET_EDIT) format_srule(buf + 2, srule); else if (cmd == IP_VS_SO_SET_ADDDEST || cmd == IP_VS_SO_SET_DELDEST || cmd == IP_VS_SO_SET_EDITDEST) format_drule(buf + 2 + format_srule(buf + 2, srule), drule); else buf[0] = '\0'; if (buf[0]) strcat(buf, ")"); log_message(LOG_INFO, "IPVS cmd %s(%d) error: %s(%d)%s", ipvs_cmd_str(cmd), cmd, ipvs_strerror(errno), errno, buf); } return result; } /* Note: This function may be called in the context of the vrrp child process */ void ipvs_syncd_cmd(int cmd, const struct lvs_syncd_config *config, int state, bool ignore_error) { ipvs_daemon_t daemonrule; memset(&daemonrule, 0, sizeof(ipvs_daemon_t)); /* prepare user rule */ if (config) { daemonrule.user.syncid = (int)config->syncid; if (cmd == IPVS_STARTDAEMON) { strcpy_safe(daemonrule.user.mcast_ifn, config->ifname); #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ if (config->sync_maxlen) daemonrule.sync_maxlen = config->sync_maxlen; if (config->mcast_port) daemonrule.mcast_port = config->mcast_port; if (config->mcast_ttl) daemonrule.mcast_ttl = config->mcast_ttl; if (config->mcast_group.ss_family == AF_INET) { daemonrule.mcast_af = AF_INET; daemonrule.mcast_group.ip = PTR_CAST_CONST(struct sockaddr_in, &config->mcast_group)->sin_addr.s_addr; } else if (config->mcast_group.ss_family == AF_INET6) { daemonrule.mcast_af = AF_INET6; memcpy(&daemonrule.mcast_group.in6, &PTR_CAST_CONST(struct sockaddr_in6, &config->mcast_group)->sin6_addr, sizeof(daemonrule.mcast_group.in6)); } #endif } } if (state & IPVS_MASTER) { daemonrule.user.state = IP_VS_STATE_MASTER; /* Talk to the IPVS channel */ ipvs_talk(cmd, NULL, NULL, &daemonrule, ignore_error); } if (state & IPVS_BACKUP) { daemonrule.user.state = IP_VS_STATE_BACKUP; /* Talk to the IPVS channel */ ipvs_talk(cmd, NULL, NULL, &daemonrule, ignore_error); } } void ipvs_flush_cmd(void) { ipvs_talk(IP_VS_SO_SET_FLUSH, NULL, NULL, NULL, false); } /* IPVS group range rule */ static int ipvs_group_range_cmd(int cmd, ipvs_service_t *srule, ipvs_dest_t *drule, virtual_server_group_entry_t *vsg_entry) { uint32_t end; /* Set address and port */ if (vsg_entry->addr.ss_family == AF_INET6) { inet_sockaddrip6(&vsg_entry->addr, &srule->nf_addr.in6); end = PTR_CAST(struct sockaddr_in6, &vsg_entry->addr_end)->sin6_addr.s6_addr16[7]; } else { srule->nf_addr.ip = inet_sockaddrip4(&vsg_entry->addr); end = PTR_CAST(struct sockaddr_in, &vsg_entry->addr_end)->sin_addr.s_addr; } srule->af = vsg_entry->addr.ss_family; srule->user.netmask = (srule->af == AF_INET6) ? 128 : ((uint32_t) 0xffffffff); /* Process the whole range */ do { /* Talk to the IPVS channel */ if (ipvs_talk(cmd, srule, drule, NULL, false)) return -1; if (srule->af == AF_INET) { if (srule->nf_addr.ip == end) break; srule->nf_addr.ip += htonl(1); } else { if (srule->nf_addr.in6.s6_addr16[7] == end) break; srule->nf_addr.in6.s6_addr16[7] = htons(ntohs(srule->nf_addr.in6.s6_addr16[7]) + 1); } } while (true); return 0; } /* set IPVS group rules */ static bool is_vsge_alive(virtual_server_group_entry_t *vsge, virtual_server_t *vs) { if (vsge->is_fwmark) { if (vs->af || vsge->fwm_family == AF_INET) return !!vsge->fwm4_alive; else return !!vsge->fwm6_alive; } else if (vs->service_type == IPPROTO_TCP) return !!vsge->tcp_alive; else if (vs->service_type == IPPROTO_UDP) return !!vsge->udp_alive; else return !!vsge->sctp_alive; } static void update_vsge_alive_count(virtual_server_group_entry_t *vsge, const virtual_server_t *vs, bool up) { unsigned *alive_p; if (vsge->is_fwmark) { if (vs->af == AF_INET) alive_p = &vsge->fwm4_alive; else alive_p = &vsge->fwm6_alive; } else if (vs->service_type == IPPROTO_TCP) alive_p = &vsge->tcp_alive; else if (vs->service_type == IPPROTO_UDP) alive_p = &vsge->udp_alive; else alive_p = &vsge->sctp_alive; if (up) (*alive_p)++; else (*alive_p)--; } static void set_vsge_alive(virtual_server_group_entry_t *vsge, const virtual_server_t *vs) { update_vsge_alive_count(vsge, vs, true); } void unset_vsge_alive(virtual_server_group_entry_t *vsge, const virtual_server_t *vs) { update_vsge_alive_count(vsge, vs, false); } static bool ipvs_change_needed(int cmd, virtual_server_group_entry_t *vsge, virtual_server_t *vs, real_server_t *rs) { unsigned count; if (cmd == IP_VS_SO_SET_ADD) return !is_vsge_alive(vsge, vs); else if (cmd == IP_VS_SO_SET_DEL) { count = vsge->is_fwmark ? (vs->af == AF_INET ? vsge->fwm4_alive : vsge->fwm6_alive) : vs->service_type == IPPROTO_TCP ? vsge->tcp_alive : vs->service_type == IPPROTO_UDP ? vsge->udp_alive : vsge->sctp_alive; return (count == 0); } else if (cmd == IP_VS_SO_SET_ADDDEST) return !rs->alive; else if (cmd == IP_VS_SO_SET_DELDEST) return rs->alive; else /* cmd == IP_VS_SO_SET_EDITDEST */ return true; } static void ipvs_set_vsge_alive_state(int cmd, virtual_server_group_entry_t *vsge, virtual_server_t *vs) { if (cmd == IP_VS_SO_SET_ADDDEST) set_vsge_alive(vsge, vs); else if (cmd == IP_VS_SO_SET_DELDEST) unset_vsge_alive(vsge, vs); } static int ipvs_group_cmd(int cmd, ipvs_service_t *srule, ipvs_dest_t *drule, virtual_server_t *vs, real_server_t *rs) { virtual_server_group_t *vsg = vs->vsg; virtual_server_group_entry_t *vsg_entry; /* return if jointure fails */ if (!vsg) return 0; /* visit addr_range list */ list_for_each_entry(vsg_entry, &vsg->addr_range, e_list) { if (cmd == IP_VS_SO_SET_ADD && reload && vsg_entry->reloaded) continue; if (ipvs_change_needed(cmd, vsg_entry, vs, rs)) { srule->user.port = inet_sockaddrport(&vsg_entry->addr); if (rs) { if (rs->forwarding_method != IP_VS_CONN_F_MASQ) drule->user.port = srule->user.port; else drule->user.port = inet_sockaddrport(&rs->addr); } if (ipvs_group_range_cmd(cmd, srule, drule, vsg_entry)) return -1; } if (cmd == IP_VS_SO_SET_ADDDEST || cmd == IP_VS_SO_SET_DELDEST) ipvs_set_vsge_alive_state(cmd, vsg_entry, vs); } /* visit vfwmark list */ memset(&srule->nf_addr, 0, sizeof(srule->nf_addr)); srule->user.port = 0; if (rs) { if (rs->forwarding_method != IP_VS_CONN_F_MASQ) drule->user.port = 0; else drule->user.port = inet_sockaddrport(&rs->addr); } list_for_each_entry(vsg_entry, &vsg->vfwmark, e_list) { if (cmd == IP_VS_SO_SET_ADD && reload && vsg_entry->reloaded) continue; srule->user.fwmark = vsg_entry->vfwmark; if (vsg_entry->fwm_family != AF_UNSPEC) srule->af = vsg_entry->fwm_family; else if (vs->af != AF_UNSPEC) srule->af = vs->af; else srule->af = AF_INET; // We default to IPv4 if cannot determine the family srule->user.netmask = (srule->af == AF_INET6) ? 128 : ((uint32_t) 0xffffffff); /* Talk to the IPVS channel */ if (ipvs_change_needed(cmd, vsg_entry, vs, rs)) { if (ipvs_talk(cmd, srule, drule, NULL, false)) return -1; } ipvs_set_vsge_alive_state(cmd, vsg_entry, vs); } return 0; } /* Fill IPVS rule with root vs infos */ static void ipvs_set_srule(int cmd, ipvs_service_t *srule, virtual_server_t *vs) { /* Clean service rule */ memset(srule, 0, sizeof(ipvs_service_t)); strcpy_safe(srule->user.sched_name, vs->sched); srule->af = (vs->vsg && vs->af == AF_UNSPEC) ? (vs->vsg->have_ipv4) ? AF_INET : AF_INET6 : vs->af; srule->user.flags = vs->flags; srule->user.netmask = (srule->af == AF_INET6) ? 128 : ((uint32_t) 0xffffffff); srule->user.protocol = vs->service_type; if (vs->persistence_timeout && (cmd == IP_VS_SO_SET_ADD || cmd == IP_VS_SO_SET_DEL || cmd == IP_VS_SO_SET_EDIT)) { srule->user.timeout = vs->persistence_timeout; srule->user.flags |= IP_VS_SVC_F_PERSISTENT; if (vs->persistence_granularity != 0xffffffff) srule->user.netmask = vs->persistence_granularity; strcpy(srule->pe_name, vs->pe_name); } } /* Fill IPVS rule with rs infos */ static void ipvs_set_drule(int cmd, ipvs_dest_t *drule, real_server_t * rs) { if (cmd != IP_VS_SO_SET_ADDDEST && cmd != IP_VS_SO_SET_DELDEST && cmd != IP_VS_SO_SET_EDITDEST) return; /* Clean target rule */ memset(drule, 0, sizeof(ipvs_dest_t)); drule->af = rs->addr.ss_family; if (rs->addr.ss_family == AF_INET6) inet_sockaddrip6(&rs->addr, &drule->nf_addr.in6); else drule->nf_addr.ip = inet_sockaddrip4(&rs->addr); drule->user.port = inet_sockaddrport(&rs->addr); drule->user.conn_flags = rs->forwarding_method; drule->user.weight = real_weight(rs->effective_weight); drule->user.u_threshold = rs->u_threshold; drule->user.l_threshold = rs->l_threshold; #ifdef _HAVE_IPVS_TUN_TYPE_ drule->tun_type = rs->tun_type; drule->tun_port = rs->tun_port; #ifdef _HAVE_IPVS_TUN_CSUM_ drule->tun_flags = rs->tun_flags; #endif #endif } /* Set/Remove a RS from a VS */ int ipvs_cmd(int cmd, virtual_server_t *vs, real_server_t *rs) { ipvs_service_t srule; ipvs_dest_t drule; int ret; #ifdef _WITH_NFTABLES_ proto_index_t proto_index; #endif /* Allocate the room */ ipvs_set_srule(cmd, &srule, vs); if (rs) { ipvs_set_drule(cmd, &drule, rs); /* Does the service use inhibit flag ? */ if (cmd == IP_VS_SO_SET_DELDEST && rs->inhibit) { drule.user.weight = 0; cmd = IP_VS_SO_SET_EDITDEST; } else if (cmd == IP_VS_SO_SET_ADDDEST && rs->inhibit && rs->set) cmd = IP_VS_SO_SET_EDITDEST; /* Set flag */ else if (cmd == IP_VS_SO_SET_ADDDEST && !rs->set) { rs->set = true; if (rs->inhibit && rs->num_failed_checkers) drule.user.weight = 0; } else if (cmd == IP_VS_SO_SET_DELDEST && rs->set) rs->set = false; } /* Set vs rule and send to kernel */ #ifdef _WITH_NFTABLES_ if (VS_USES_VSG_AUTO_FWMARK(vs)) proto_index = protocol_to_index(vs->service_type); else proto_index = PROTO_INDEX_NONE; #endif if (vs->vsg) { #ifdef _WITH_NFTABLES_ if (cmd == IP_VS_SO_SET_ADD && VS_USES_VSG_AUTO_FWMARK(vs) && !vs->vsg->auto_fwmark[proto_index]) { vs->vsg->auto_fwmark[proto_index] = set_vs_fwmark(vs); } else if (proto_index == PROTO_INDEX_NONE || !vs->vsg->auto_fwmark[proto_index]) #endif return ipvs_group_cmd(cmd, &srule, &drule, vs, rs); } if (vs->vfwmark #ifdef _WITH_NFTABLES_ || VS_USES_VSG_AUTO_FWMARK(vs) #endif ) { #ifdef _WITH_NFTABLES_ srule.user.fwmark = VS_USES_VSG_AUTO_FWMARK(vs) ? vs->vsg->auto_fwmark[proto_index] : vs->vfwmark; #else srule.user.fwmark = vs->vfwmark; #endif if (rs && rs->forwarding_method != IP_VS_CONN_F_MASQ) drule.user.port = 0; } else { if (vs->af == AF_INET6) inet_sockaddrip6(&vs->addr, &srule.nf_addr.in6); else srule.nf_addr.ip = inet_sockaddrip4(&vs->addr); srule.user.port = inet_sockaddrport(&vs->addr); if (rs && rs->forwarding_method != IP_VS_CONN_F_MASQ) drule.user.port = srule.user.port; } /* Talk to the IPVS channel */ ret = ipvs_talk(cmd, &srule, &drule, NULL, false); #ifdef _WITH_NFTABLES_ if (!ret && vs->vsg && vs->af == AF_UNSPEC && vs->vsg->have_ipv4 && vs->vsg->have_ipv6 && proto_index != PROTO_INDEX_NONE && vs->vsg->auto_fwmark[proto_index]) { srule.af = AF_INET6; srule.user.netmask = 128; ret = ipvs_talk(cmd, &srule, &drule, NULL, false); } #endif return ret; } /* at reload, add alive destinations to the newly created vsge */ void ipvs_group_sync_entry(virtual_server_t *vs, virtual_server_group_entry_t *vsge) { real_server_t *rs; ipvs_service_t srule; ipvs_dest_t drule; ipvs_set_srule(IP_VS_SO_SET_ADDDEST, &srule, vs); #ifdef _WITH_NFTABLES_ if (VS_USES_VSG_AUTO_FWMARK(vs)) srule.user.fwmark = vs->vsg->auto_fwmark[protocol_to_index(vs->service_type)]; else #endif if (vsge->is_fwmark) srule.user.fwmark = vsge->vfwmark; else srule.user.port = inet_sockaddrport(&vsge->addr); /* Process realserver queue */ list_for_each_entry(rs, &vs->rs, e_list) { // ??? What if !quorum_state_up? if (rs->reloaded && (rs->alive || (rs->inhibit && rs->set))) { /* Prepare the IPVS drule */ ipvs_set_drule(IP_VS_SO_SET_ADDDEST, &drule, rs); drule.user.weight = ((rs->inhibit && !rs->alive) || vs->s_svr->alive) ? 0 : real_weight(rs->effective_weight); if (rs->forwarding_method != IP_VS_CONN_F_MASQ) drule.user.port = inet_sockaddrport(&vsge->addr); else drule.user.port = inet_sockaddrport(&rs->addr); /* Set vs rule */ if (srule.user.fwmark) { /* Talk to the IPVS channel */ ipvs_talk(IP_VS_SO_SET_ADDDEST, &srule, &drule, NULL, false); } else ipvs_group_range_cmd(IP_VS_SO_SET_ADDDEST, &srule, &drule, vsge); ipvs_set_vsge_alive_state(IP_VS_SO_SET_ADDDEST, vsge, vs); } } if (vs->s_svr && vs->s_svr->reloaded && vs->s_svr->set) { ipvs_set_drule(IP_VS_SO_SET_ADDDEST, &drule, vs->s_svr); drule.user.weight = vs->s_svr->alive ? real_weight(vs->s_svr->effective_weight) : 0; if (vs->s_svr->forwarding_method != IP_VS_CONN_F_MASQ) drule.user.port = inet_sockaddrport(&vsge->addr); else drule.user.port = inet_sockaddrport(&vs->s_svr->addr); ipvs_group_range_cmd(IP_VS_SO_SET_ADDDEST, &srule, &drule, vsge); ipvs_set_vsge_alive_state(IP_VS_SO_SET_ADDDEST, vsge, vs); } } /* Remove a specific vs group entry */ void ipvs_group_remove_entry(virtual_server_t *vs, virtual_server_group_entry_t *vsge) { real_server_t *rs; ipvs_service_t srule; ipvs_dest_t drule; #ifdef _WITH_NFTABLES_ /* Prepare target rules */ if (VS_USES_VSG_AUTO_FWMARK(vs)) { /* Remove the fwmark entry(s) */ remove_vs_fwmark_entry(vs, vsge); // TODO - Is this trying to remove the VS itself? Check similar at end of function // if (!is_vsge_alive(vsge, vs)) // remove_vs_fwmark_entry(vs, vsge); return; } #endif ipvs_set_srule(IP_VS_SO_SET_DELDEST, &srule, vs); #ifdef _WITH_NFTABLES_ if (VS_USES_VSG_AUTO_FWMARK(vs)) srule.user.fwmark = vs->vsg->auto_fwmark[protocol_to_index(vs->service_type)]; else #endif if (vsge->is_fwmark) srule.user.fwmark = vsge->vfwmark; else srule.user.port = inet_sockaddrport(&vsge->addr); /* Process realserver queue */ list_for_each_entry(rs, &vs->rs, e_list) { if (rs->set) { if (global_data->lvs_flush_on_stop == LVS_NO_FLUSH) { /* Setting IPVS drule */ ipvs_set_drule(IP_VS_SO_SET_DELDEST, &drule, rs); if (rs->forwarding_method != IP_VS_CONN_F_MASQ) drule.user.port = inet_sockaddrport(&vsge->addr); else drule.user.port = inet_sockaddrport(&rs->addr); /* Delete rs rule */ if (srule.user.fwmark) { /* Talk to the IPVS channel */ ipvs_talk(IP_VS_SO_SET_DELDEST, &srule, &drule, NULL, false); } else ipvs_group_range_cmd(IP_VS_SO_SET_DELDEST, &srule, &drule, vsge); } ipvs_set_vsge_alive_state(IP_VS_SO_SET_DELDEST, vsge, vs); } } if (vs->s_svr && vs->s_svr->set) { if (global_data->lvs_flush_on_stop == LVS_NO_FLUSH) { ipvs_set_drule(IP_VS_SO_SET_ADDDEST, &drule, vs->s_svr); if (vs->s_svr->forwarding_method != IP_VS_CONN_F_MASQ) drule.user.port = inet_sockaddrport(&vsge->addr); else drule.user.port = inet_sockaddrport(&vs->s_svr->addr); ipvs_group_range_cmd(IP_VS_SO_SET_DELDEST, &srule, &drule, vsge); } ipvs_set_vsge_alive_state(IP_VS_SO_SET_DELDEST, vsge, vs); } /* Remove VS entry if this is the last VS using it */ if (!is_vsge_alive(vsge, vs)) { if (srule.user.fwmark) ipvs_talk(IP_VS_SO_SET_DEL, &srule, NULL, NULL, false); else ipvs_group_range_cmd(IP_VS_SO_SET_DEL, &srule, NULL, vsge); } } #ifdef _WITH_NFTABLES_ void remove_fwmark_vs(virtual_server_t *vs, int family) { ipvs_service_t srule; ipvs_set_srule(IP_VS_SO_SET_DEL, &srule, vs); srule.af = family; srule.user.fwmark = vs->vsg->auto_fwmark[protocol_to_index(vs->service_type)]; srule.user.netmask = (family == AF_INET6) ? 128 : ((uint32_t) 0xffffffff); ipvs_talk(IP_VS_SO_SET_DEL, &srule, NULL, NULL, false); } void add_fwmark_vs(virtual_server_t *vs, int family) { ipvs_service_t srule; ipvs_set_srule(IP_VS_SO_SET_DEL, &srule, vs); srule.af = family; srule.user.fwmark = vs->vsg->auto_fwmark[protocol_to_index(vs->service_type)]; srule.user.netmask = (family == AF_INET6) ? 128 : ((uint32_t) 0xffffffff); ipvs_talk(IP_VS_SO_SET_ADD, &srule, NULL, NULL, false); } #endif #ifdef _WITH_SNMP_CHECKER_ static inline bool vsd_equal(real_server_t *rs, struct ip_vs_dest_entry_app *entry) { uint32_t port; if (entry->af != AF_INET && entry->af != AF_INET6) return false; if (rs->addr.ss_family != entry->af) return false; if (!inaddr_equal(entry->af, &entry->nf_addr, entry->af == AF_INET ? (void *)&PTR_CAST(struct sockaddr_in, &rs->addr)->sin_addr : (void *)&PTR_CAST(struct sockaddr_in6, &rs->addr)->sin6_addr)) return false; port = (entry->af == AF_INET ? PTR_CAST(struct sockaddr_in, &rs->addr)->sin_port : PTR_CAST(struct sockaddr_in6, &rs->addr)->sin6_port); if (port && port != entry->user.port) return false; return true; } static void ipvs_update_vs_stats(virtual_server_t *vs, uint16_t af, uint32_t fwmark, union nf_inet_addr *nfaddr, uint16_t port) { ipvs_service_entry_t *serv; if (!(serv = ipvs_get_service(fwmark, af, vs->service_type, nfaddr, port))) return; /* Update virtual server stats */ vs->stats.conns += serv->ip_vs_stats.conns; vs->stats.inpkts += serv->ip_vs_stats.inpkts; vs->stats.outpkts += serv->ip_vs_stats.outpkts; vs->stats.inbytes += serv->ip_vs_stats.inbytes; vs->stats.outbytes += serv->ip_vs_stats.outbytes; vs->stats.cps += serv->ip_vs_stats.cps; vs->stats.inpps += serv->ip_vs_stats.inpps; vs->stats.outpps += serv->ip_vs_stats.outpps; vs->stats.inbps += serv->ip_vs_stats.inbps; vs->stats.outbps += serv->ip_vs_stats.outbps; vs->num_dests = serv->user.num_dests; // Only needed if using old socket interface FREE(serv); } static void ipvs_update_rs_stats(virtual_server_t *vs, uint16_t af, uint32_t fwmark, union nf_inet_addr *nfaddr, uint16_t port) { struct ip_vs_get_dests_app *dests; real_server_t *rs, *rs_match; unsigned int i; /* Get real servers */ dests = ipvs_get_dests(fwmark, af, vs->service_type, nfaddr, port, vs->num_dests ? vs->num_dests : vs->rs_cnt + !!vs->s_svr); if (!dests) return; for (i = 0; i < dests->user.num_dests; i++) { rs = NULL; /* Is it the sorry server? */ if (vs->s_svr && vsd_equal(vs->s_svr, &dests->user.entrytable[i])) rs = vs->s_svr; else { /* Search for a match in the list of real servers */ rs_match = NULL; list_for_each_entry(rs, &vs->rs, e_list) { if (vsd_equal(rs, &dests->user.entrytable[i])) { rs_match = rs; break; } } if (!rs_match) rs = NULL; } if (!rs) continue; rs->activeconns += dests->user.entrytable[i].user.activeconns; rs->inactconns += dests->user.entrytable[i].user.inactconns; rs->persistconns += dests->user.entrytable[i].user.persistconns; rs->stats.conns += dests->user.entrytable[i].ip_vs_stats.conns; rs->stats.inpkts += dests->user.entrytable[i].ip_vs_stats.inpkts; rs->stats.outpkts += dests->user.entrytable[i].ip_vs_stats.outpkts; rs->stats.inbytes += dests->user.entrytable[i].ip_vs_stats.inbytes; rs->stats.outbytes += dests->user.entrytable[i].ip_vs_stats.outbytes; rs->stats.cps += dests->user.entrytable[i].ip_vs_stats.cps; rs->stats.inpps += dests->user.entrytable[i].ip_vs_stats.inpps; rs->stats.outpps += dests->user.entrytable[i].ip_vs_stats.outpps; rs->stats.inbps += dests->user.entrytable[i].ip_vs_stats.inbps; rs->stats.outbps += dests->user.entrytable[i].ip_vs_stats.outbps; } FREE(dests); } /* Update statistics for a given virtual server. The update is only done if we need refreshing. */ void ipvs_vs_update_stats(virtual_server_t *vs) { virtual_server_group_entry_t *vsg_entry; uint32_t addr_ip, addr_end; uint16_t port; union nf_inet_addr nfaddr; struct timespec cur_time; uint16_t af; clock_gettime(CLOCK_MONOTONIC, &cur_time); if ((unsigned long)((cur_time.tv_sec - vs->vs_stats_last_updated.tv_sec) * TIMER_HZ + cur_time.tv_nsec / (NSEC_PER_SEC / TIMER_HZ) - vs->vs_stats_last_updated.tv_nsec / (NSEC_PER_SEC / TIMER_HZ)) < global_data->snmp_vs_stats_update_interval) return; vs->vs_stats_last_updated = cur_time; /* Reset stats */ memset(&vs->stats, 0, sizeof(vs->stats)); /* Update the stats */ if (vs->vsg) { for (af = (vs->vsg->have_ipv4) ? AF_INET : AF_INET6; af != AF_UNSPEC; af = af == AF_INET && vs->vsg->have_ipv6 ? AF_INET6 : AF_UNSPEC) { #ifdef _WITH_NFTABLES_ if (VS_USES_VSG_AUTO_FWMARK(vs)) ipvs_update_vs_stats(vs, af, vs->vsg->auto_fwmark[protocol_to_index(vs->service_type)], NULL, 0); else #endif { list_for_each_entry(vsg_entry, &vs->vsg->vfwmark, e_list) ipvs_update_vs_stats(vs, af, vsg_entry->vfwmark, NULL, 0); list_for_each_entry(vsg_entry, &vs->vsg->addr_range, e_list) { addr_ip = (vsg_entry->addr.ss_family == AF_INET6) ? ntohs(PTR_CAST(struct sockaddr_in6, &vsg_entry->addr)->sin6_addr.s6_addr16[7]) : ntohl(PTR_CAST(struct sockaddr_in, &vsg_entry->addr)->sin_addr.s_addr); addr_end = (vsg_entry->addr.ss_family == AF_INET6) ? ntohs(PTR_CAST(struct sockaddr_in6, &vsg_entry->addr_end)->sin6_addr.s6_addr16[7]) : ntohl(PTR_CAST(struct sockaddr_in, &vsg_entry->addr_end)->sin_addr.s_addr); if (vsg_entry->addr.ss_family == AF_INET6) inet_sockaddrip6(&vsg_entry->addr, &nfaddr.in6); port = inet_sockaddrport(&vsg_entry->addr); do { if (vsg_entry->addr.ss_family == AF_INET6) nfaddr.in6.s6_addr16[7] = htons(addr_ip); else nfaddr.ip = htonl(addr_ip); ipvs_update_vs_stats(vs, af, 0, &nfaddr, port); // This doesn't work for /111 say } while (addr_ip++ != addr_end); } } } } else if (vs->vfwmark) { ipvs_update_vs_stats(vs, vs->af, vs->vfwmark, NULL, 0); } else { memcpy(&nfaddr, (vs->addr.ss_family == AF_INET6) ? (void*)(&PTR_CAST(struct sockaddr_in6, &vs->addr)->sin6_addr) : (void*)(&PTR_CAST(struct sockaddr_in, &vs->addr)->sin_addr), sizeof(nfaddr)); ipvs_update_vs_stats(vs, vs->af, 0, &nfaddr, inet_sockaddrport(&vs->addr)); } } /* Update statistics for a given real server. The update is only done if we need refreshing. */ void ipvs_rs_update_stats(virtual_server_t *vs) { virtual_server_group_entry_t *vsg_entry; uint32_t addr_ip, addr_end; uint16_t port; union nf_inet_addr nfaddr; real_server_t *rs; struct timespec cur_time; uint16_t af; clock_gettime(CLOCK_MONOTONIC, &cur_time); if ((unsigned long)((cur_time.tv_sec - vs->rs_stats_last_updated.tv_sec) * TIMER_HZ + cur_time.tv_nsec / (NSEC_PER_SEC / TIMER_HZ) - vs->rs_stats_last_updated.tv_nsec / (NSEC_PER_SEC / TIMER_HZ)) < global_data->snmp_rs_stats_update_interval) return; vs->rs_stats_last_updated = cur_time; /* Reset stats */ if (vs->s_svr) { memset(&vs->s_svr->stats, 0, sizeof(vs->s_svr->stats)); vs->s_svr->activeconns = vs->s_svr->inactconns = vs->s_svr->persistconns = 0; } list_for_each_entry(rs, &vs->rs, e_list) { memset(&rs->stats, 0, sizeof(rs->stats)); rs->activeconns = rs->inactconns = rs->persistconns = 0; } /* Update the stats */ if (vs->vsg) { for (af = (vs->vsg->have_ipv4) ? AF_INET : AF_INET6; af != AF_UNSPEC; af = af == AF_INET && vs->vsg->have_ipv6 ? AF_INET6 : AF_UNSPEC) { #ifdef _WITH_NFTABLES_ if (VS_USES_VSG_AUTO_FWMARK(vs)) ipvs_update_rs_stats(vs, af, vs->vsg->auto_fwmark[protocol_to_index(vs->service_type)], NULL, 0); else #endif { list_for_each_entry(vsg_entry, &vs->vsg->vfwmark, e_list) ipvs_update_rs_stats(vs, af, vsg_entry->vfwmark, NULL, 0); list_for_each_entry(vsg_entry, &vs->vsg->addr_range, e_list) { addr_ip = (vsg_entry->addr.ss_family == AF_INET6) ? ntohs(PTR_CAST(struct sockaddr_in6, &vsg_entry->addr)->sin6_addr.s6_addr16[7]) : ntohl(PTR_CAST(struct sockaddr_in, &vsg_entry->addr)->sin_addr.s_addr); addr_end = (vsg_entry->addr.ss_family == AF_INET6) ? ntohs(PTR_CAST(struct sockaddr_in6, &vsg_entry->addr_end)->sin6_addr.s6_addr16[7]) : ntohl(PTR_CAST(struct sockaddr_in, &vsg_entry->addr_end)->sin_addr.s_addr); if (vsg_entry->addr.ss_family == AF_INET6) inet_sockaddrip6(&vsg_entry->addr, &nfaddr.in6); port = inet_sockaddrport(&vsg_entry->addr); do { if (vsg_entry->addr.ss_family == AF_INET6) nfaddr.in6.s6_addr16[7] = htons(addr_ip); else nfaddr.ip = htonl(addr_ip); ipvs_update_rs_stats(vs, af, 0, &nfaddr, port); // This doesn't work for /111 say } while (addr_ip++ != addr_end); } } } } else if (vs->vfwmark) { ipvs_update_rs_stats(vs, vs->af, vs->vfwmark, NULL, 0); } else { memcpy(&nfaddr, (vs->addr.ss_family == AF_INET6) ? (void*)(&PTR_CAST(struct sockaddr_in6, &vs->addr)->sin6_addr) : (void*)(&PTR_CAST(struct sockaddr_in, &vs->addr)->sin_addr), sizeof(nfaddr)); ipvs_update_rs_stats(vs, vs->af, 0, &nfaddr, inet_sockaddrport(&vs->addr)); } } #endif /* _WITH_SNMP_CHECKER_ */ bool ipvs_syncd_changed(const struct lvs_syncd_config *old, const struct lvs_syncd_config *new) { return (old->syncid != new->syncid || strcmp(old->ifname, new->ifname) #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ || old->sync_maxlen != new->sync_maxlen || old->mcast_port != new->mcast_port || old->mcast_ttl != new->mcast_ttl || !sockstorage_equal(&old->mcast_group, &new->mcast_group) #endif ); } #ifdef _WITH_VRRP_ /* * Common IPVS functions */ /* Note: This function is called in the context of the vrrp child process, not the checker process */ void ipvs_syncd_master(const struct lvs_syncd_config *config) { ipvs_syncd_cmd(IPVS_STOPDAEMON, config, IPVS_BACKUP, false); ipvs_syncd_cmd(IPVS_STARTDAEMON, config, IPVS_MASTER, false); } /* Note: This function is called in the context of the vrrp child process, not the checker process */ void ipvs_syncd_backup(const struct lvs_syncd_config *config) { ipvs_syncd_cmd(IPVS_STOPDAEMON, config, IPVS_MASTER, false); ipvs_syncd_cmd(IPVS_STARTDAEMON, config, IPVS_BACKUP, false); } #endif keepalived-2.3.3/keepalived/check/check_ping.c0000664000175000017500000003342714661620303014751 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: PING checker. * * Author: Jie Liu, * * 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) 2019-2019 Alexandre Cassen, */ #include "config.h" /* system includes */ #include #include #include #include #include #include #include #include "check_ping.h" #include "check_api.h" #include "logger.h" #include "layer4.h" #include "parser.h" #include "smtp.h" #include "ipwrapper.h" #include "check_parser.h" #define ICMP_BUFSIZE 128 #define SOCK_RECV_BUFF 128*1024 static const char * const ping_group_range = "/proc/sys/net/ipv4/ping_group_range"; static gid_t save_gid_min; static bool checked_ping_group_range; static void icmp_connect_thread(thread_ref_t); static void icmp_ping_thread(thread_ref_t); bool set_ping_group_range(bool set) { char buf[10 + 1 + 10 + 1 + 1]; /* 20000000004294967295 */ int fd; ssize_t len, ret; unsigned long val[2]; char *endptr; if (set == checked_ping_group_range) return true; fd = open(ping_group_range, O_RDWR); if (fd == -1) return false; len = read(fd, buf, sizeof(buf)); if (len == -1 || len == sizeof(buf)) { close(fd); return false; } buf[len] = '\0'; val[0] = strtoul(buf, &endptr, 10); #if ULONG_MAX >= 1UL << 32 if (val[0] >= 1UL << 32 || (*endptr != '\t' && *endptr != ' ')) { close(fd); return false; } #endif val[1] = strtol(endptr + 1, &endptr, 10); #if ULONG_MAX >= 1UL << 32 if (val[1] >= 1UL << 32 || *endptr != '\n') { close(fd); return false; } #endif checked_ping_group_range = set; if ((set && val[0] == 0) || (!set && val[0] == save_gid_min)) { close(fd); return true; } if (set && val[0] > 1 && val[1] > 1) log_message(LOG_INFO, "Warning: %s being expanded from %lu %lu to 0 %lu", ping_group_range, val[0], val[1], val[1]); len = sprintf(buf, "%u\t%lu\t\n", set ? 0 : save_gid_min, val[1]); lseek(fd, 0, SEEK_SET); ret = write(fd, buf, len); if (ret == -1) log_message(LOG_INFO, "Write %s failed - errno %d", ping_group_range, errno); else if (ret != len) log_message(LOG_INFO, "Write %s wrote %zd bytes instead of %zd", ping_group_range, ret, len); close(fd); if (set) save_gid_min = val[0]; return true; } /* Configuration stream handling */ static void free_ping_check(checker_t *checker) { FREE(checker->co); FREE_PTR(checker->data); FREE(checker); } static void dump_ping_check(FILE *fp, const checker_t *checker) { ping_check_t *ping_checker = CHECKER_ARG(checker); conf_write(fp, " Keepalive method = PING_CHECK"); conf_write(fp, " ICMP seq no = %u", ping_checker->seq_no); } static bool compare_ping_check(const checker_t *a, checker_t *b) { return compare_conn_opts(a->co, b->co); } static const checker_funcs_t ping_checker_funcs = { CHECKER_PING, free_ping_check, dump_ping_check, compare_ping_check, NULL }; static void ping_check_handler(__attribute__((unused)) const vector_t *strvec) { ping_check_t *ping_check = sizeof(ping_check_t) ? MALLOC(sizeof (ping_check_t)) : NULL; /* queue new checker */ queue_checker(&ping_checker_funcs, icmp_connect_thread, ping_check, CHECKER_NEW_CO(), true); if (!checked_ping_group_range) set_ping_group_range(true); } static void ping_check_end_handler(void) { if (!check_conn_opts(current_checker->co)) { dequeue_new_checker(); return; } } void install_ping_check_keyword(void) { vpp_t check_ptr; /* We don't want some common keywords */ install_keyword("PING_CHECK", &ping_check_handler); check_ptr = install_sublevel(VPP ¤t_checker); install_checker_common_keywords(true); install_level_end_handler(ping_check_end_handler); install_sublevel_end(check_ptr); } static enum connect_result ping_it(int fd, checker_t *checker, conn_opts_t* co) { ping_check_t *ping_checker = CHECKER_ARG(checker); struct icmphdr *icmp_hdr; char send_buf[sizeof(*icmp_hdr) + ICMP_BUFSIZE] __attribute__((aligned(__alignof__(struct icmphdr)))); set_buf(send_buf + sizeof(*icmp_hdr), ICMP_BUFSIZE); icmp_hdr = PTR_CAST(struct icmphdr, send_buf); memset(icmp_hdr, 0, sizeof(*icmp_hdr)); icmp_hdr->type = ICMP_ECHO; icmp_hdr->un.echo.sequence = htons(++ping_checker->seq_no); if (sendto(fd, send_buf, sizeof(send_buf), 0, PTR_CAST(struct sockaddr, &co->dst), sizeof(struct sockaddr)) < 0) { log_message(LOG_INFO, "send ICMP packet fail"); return connect_error; } return connect_success; } static enum connect_result recv_it(int fd, checker_t *checker) { ping_check_t *ping_checker = CHECKER_ARG(checker); ssize_t len; const struct icmphdr *icmp_hdr; char recv_buf[sizeof(*icmp_hdr) + ICMP_BUFSIZE] __attribute__((aligned(__alignof__(struct icmphdr)))); len = recv(fd, recv_buf, sizeof(recv_buf), 0); if (len < 0) { log_message(LOG_INFO, "recv ICMP packet error"); return connect_error; } if ((size_t)len < sizeof(*icmp_hdr)) { log_message(LOG_INFO, "Error, got short ICMP packet, %zd bytes", len); return connect_error; } icmp_hdr = PTR_CAST_CONST(struct icmphdr, recv_buf); if (icmp_hdr->type != ICMP_ECHOREPLY) { log_message(LOG_INFO, "Got ICMP packet with type 0x%x", icmp_hdr->type); return connect_error; } if (ntohs(icmp_hdr->un.echo.sequence) != ping_checker->seq_no) { log_message(LOG_INFO, "Ping reply expected seq no %u, received %u", ping_checker->seq_no, ntohs(icmp_hdr->un.echo.sequence)); return connect_error; } return connect_success; } static enum connect_result ping6_it(int fd, checker_t *checker, conn_opts_t* co) { ping_check_t *ping_checker = CHECKER_ARG(checker); struct icmp6_hdr* icmp6_hdr; char send_buf[sizeof(*icmp6_hdr) + ICMP_BUFSIZE] __attribute__((aligned(__alignof__(struct icmp6_hdr)))); set_buf(send_buf + sizeof(*icmp6_hdr), ICMP_BUFSIZE); icmp6_hdr = PTR_CAST(struct icmp6_hdr, &send_buf); memset(icmp6_hdr, 0, sizeof(*icmp6_hdr)); icmp6_hdr->icmp6_type = ICMP6_ECHO_REQUEST; icmp6_hdr->icmp6_seq = htons(++ping_checker->seq_no); if (sendto(fd, send_buf, sizeof(send_buf), 0, PTR_CAST(struct sockaddr, &co->dst), sizeof(struct sockaddr_in6)) < 0) { log_message(LOG_INFO, "send ICMPv6 packet fail - errno %d", errno); return connect_error; } return connect_success; } static enum connect_result recv6_it(int fd, checker_t *checker) { ping_check_t *ping_checker = CHECKER_ARG(checker); ssize_t len; const struct icmp6_hdr* icmp6_hdr; char recv_buf[sizeof (*icmp6_hdr) + ICMP_BUFSIZE] __attribute__((aligned(__alignof__(struct icmp6_hdr)))); len = recv(fd, recv_buf, sizeof(recv_buf), 0); if (len < 0) { log_message(LOG_INFO, "recv ICMPv6 packet error"); return connect_error; } if ((size_t)len < sizeof(*icmp6_hdr)) { log_message(LOG_INFO, "Error, got short ICMPv6 packet, %zd bytes", len); return connect_error; } icmp6_hdr = PTR_CAST_CONST(struct icmp6_hdr, recv_buf); if (icmp6_hdr->icmp6_type != ICMP6_ECHO_REPLY) { log_message(LOG_INFO, "Got ICMPv6 packet with type 0x%x", icmp6_hdr->icmp6_type); return connect_error; } if (ntohs(icmp6_hdr->icmp6_seq) != ping_checker->seq_no) { log_message(LOG_INFO, "Ping reply expected seq no %u, received %u", ping_checker->seq_no, ntohs(icmp6_hdr->icmp6_seq)); return connect_error; } return connect_success; } static void icmp_epilog(thread_ref_t thread, bool is_success) { checker_t *checker; unsigned long delay; bool checker_was_up; bool rs_was_alive; checker = THREAD_ARG(thread); delay = checker->delay_loop; if (is_success || ((checker->is_up || !checker->has_run) && checker->retry_it >= checker->retry)) { checker->retry_it = 0; if (is_success && (!checker->is_up || !checker->has_run)) { log_message(LOG_INFO, "ICMP connection to %s success." , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(UP, checker); if (checker->rs->smtp_alert && !checker_was_up && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> ICMP CHECK succeed on service <="); } else if (!is_success && (checker->is_up || !checker->has_run)) { if (checker->retry && checker->has_run) log_message(LOG_INFO , "ICMP CHECK on service %s of %s failed after %u retries." , FMT_CHK(checker), FMT_VS(checker->vs) , checker->retry); else log_message(LOG_INFO , "ICMP CHECK on service %s failed." , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(DOWN, checker); if (checker->rs->smtp_alert && checker_was_up && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> ICMP CHECK failed on service <="); } } else if (checker->is_up) { delay = checker->delay_before_retry; ++checker->retry_it; } checker->has_run = true; if (is_success) thread_add_read(thread->master, icmp_ping_thread, checker, thread->u.f.fd, delay, THREAD_DESTROY_CLOSE_FD); else { thread_close_fd(thread); thread_add_timer(thread->master, icmp_connect_thread, checker, delay); } } static void icmp_check_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); enum connect_result status; if (thread->type == THREAD_READ_TIMEOUT) { if (checker->is_up && (global_data->checker_log_all_failures || checker->log_all_failures)) log_message(LOG_INFO, "ICMP connection to address %s timeout.", FMT_CHK(checker)); status = connect_error; } else status = checker->co->dst.ss_family == AF_INET ? recv_it(thread->u.f.fd, checker) : recv6_it(thread->u.f.fd, checker); /* * If status = connect_success, then we start udp check with the record of icmp failed times. * Otherwise we will do the icmp connect again until it reaches the unhealthy threshold. * we handle fd uniform. */ if (status == connect_success) icmp_epilog(thread, true); else if (status == connect_error) { if (checker->is_up && thread->type != THREAD_READ_TIMEOUT && (global_data->checker_log_all_failures || checker->log_all_failures)) log_message(LOG_INFO, "ICMP connection to %s of %s failed." ,FMT_CHK(checker), FMT_VS(checker->vs)); icmp_epilog(thread, false); } } static void icmp_ping_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); conn_opts_t *co = checker->co; enum connect_result status; if (thread->type == THREAD_READ_TIMEOUT) { /* Send next ICMP echo request */ if (co->dst.ss_family == AF_INET) status = ping_it(thread->u.f.fd, checker, co); else status = ping6_it(thread->u.f.fd, checker, co); /* handle icmp send status & register check worker thread */ if (udp_icmp_check_state(thread->u.f.fd, status, thread, icmp_check_thread, co->connection_to)) icmp_epilog(thread, false); } else if (thread->type == THREAD_READY_READ_FD) { /* This shouldn't happen */ ssize_t len; char recv_buf[sizeof (struct icmp6_hdr) + ICMP_BUFSIZE] __attribute__((aligned(__alignof__(struct icmp6_hdr)))); len = recv(thread->u.f.fd, recv_buf, sizeof(recv_buf), 0); if (len > 0) log_message(LOG_INFO, "unexpected ping check receive packet"); thread_add_read_sands(thread->master, icmp_ping_thread, checker, thread->u.f.fd, &thread->sands, THREAD_DESTROY_CLOSE_FD); } else { /* THREAD_READ_ERROR */ thread_close_fd(thread); thread_add_timer_sands(thread->master, icmp_connect_thread, checker, &thread->sands); } } static void icmp_connect_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); ping_check_t *ping_checker = CHECKER_ARG(checker); conn_opts_t *co = checker->co; int fd; int size = SOCK_RECV_BUFF; if (!checker->enabled) { thread_add_timer(thread->master, icmp_connect_thread, checker, checker->delay_loop); return; } /* * If we config a real server in several virtual server, the icmp_ratelimit should be cancelled. * echo 0 > /proc/sys/net/ipv4/icmp_ratelimit */ /* * Using SOCK_DGRAM with IPPROTO_ICMP/ICMPV6 means that the kernel will insert the ICMP * id value. In order to keep the same id value for each ICMP echo request, we need to keep * the socket open. * * The alternative is to use SOCK_RAW, but then we can't ensure the uniqueness of the id * field. */ if ((fd = socket(co->dst.ss_family, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, co->dst.ss_family == AF_INET ? IPPROTO_ICMP : IPPROTO_ICMPV6)) == -1) { log_message(LOG_INFO, "ICMP%s connect fail to create socket. Rescheduling.", co->dst.ss_family == AF_INET ? "" : "v6"); thread_add_timer(thread->master, icmp_connect_thread, checker, checker->delay_loop); return; } if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size))) log_message(LOG_INFO, "setsockopt SO_RCVBUF for socket %d failed (%d) - %m", fd, errno); ping_checker->seq_no = 0; thread_add_read(thread->master, icmp_ping_thread, checker, fd, 0, THREAD_DESTROY_CLOSE_FD); } #ifdef THREAD_DUMP void register_check_ping_addresses(void) { register_thread_address("icmp_check_thread", icmp_check_thread); register_thread_address("icmp_connect_thread", icmp_connect_thread); register_thread_address("icmp_ping_thread", icmp_ping_thread); } #endif keepalived-2.3.3/keepalived/check/Makefile.in0000664000175000017500000005434714772274255014602 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2018 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : @SNMP_CHECKER_TRUE@am__append_1 = check_snmp.o @SNMP_CHECKER_TRUE@am__append_2 = check_snmp.c @NFTABLES_TRUE@am__append_3 = check_nftables.o @NFTABLES_TRUE@am__append_4 = check_nftables.c @WITH_BFD_TRUE@am__append_5 = check_bfd.o @WITH_BFD_TRUE@am__append_6 = check_bfd.c subdir = keepalived/check ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libcheck_a_AR = $(AR) $(ARFLAGS) libcheck_a_DEPENDENCIES = $(am__append_1) $(am__append_3) \ $(am__append_5) am_libcheck_a_OBJECTS = check_daemon.$(OBJEXT) check_data.$(OBJEXT) \ check_parser.$(OBJEXT) check_api.$(OBJEXT) check_tcp.$(OBJEXT) \ check_http.$(OBJEXT) check_ssl.$(OBJEXT) \ check_genhash.$(OBJEXT) check_smtp.$(OBJEXT) \ check_misc.$(OBJEXT) check_dns.$(OBJEXT) check_print.$(OBJEXT) \ ipwrapper.$(OBJEXT) ipvswrapper.$(OBJEXT) libipvs.$(OBJEXT) \ check_udp.$(OBJEXT) check_ping.$(OBJEXT) check_file.$(OBJEXT) am__EXTRA_libcheck_a_SOURCES_DIST = check_snmp.c check_nftables.c \ check_bfd.c libcheck_a_OBJECTS = $(am_libcheck_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/lib depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/check_api.Po \ ./$(DEPDIR)/check_bfd.Po ./$(DEPDIR)/check_daemon.Po \ ./$(DEPDIR)/check_data.Po ./$(DEPDIR)/check_dns.Po \ ./$(DEPDIR)/check_file.Po ./$(DEPDIR)/check_genhash.Po \ ./$(DEPDIR)/check_http.Po ./$(DEPDIR)/check_misc.Po \ ./$(DEPDIR)/check_nftables.Po ./$(DEPDIR)/check_parser.Po \ ./$(DEPDIR)/check_ping.Po ./$(DEPDIR)/check_print.Po \ ./$(DEPDIR)/check_smtp.Po ./$(DEPDIR)/check_snmp.Po \ ./$(DEPDIR)/check_ssl.Po ./$(DEPDIR)/check_tcp.Po \ ./$(DEPDIR)/check_udp.Po ./$(DEPDIR)/ipvswrapper.Po \ ./$(DEPDIR)/ipwrapper.Po ./$(DEPDIR)/libipvs.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libcheck_a_SOURCES) $(EXTRA_libcheck_a_SOURCES) DIST_SOURCES = $(libcheck_a_SOURCES) \ $(am__EXTRA_libcheck_a_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib \ $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libcheck.a libcheck_a_SOURCES = \ check_daemon.c check_data.c check_parser.c \ check_api.c check_tcp.c check_http.c check_ssl.c check_genhash.c \ check_smtp.c check_misc.c check_dns.c check_print.c \ ipwrapper.c ipvswrapper.c libipvs.c check_udp.c check_ping.c \ check_file.c EXTRA_libcheck_a_SOURCES = $(am__append_2) $(am__append_4) \ $(am__append_6) libcheck_a_LIBADD = $(am__append_1) $(am__append_3) $(am__append_5) all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/check/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/check/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libcheck.a: $(libcheck_a_OBJECTS) $(libcheck_a_DEPENDENCIES) $(EXTRA_libcheck_a_DEPENDENCIES) $(AM_V_at)-rm -f libcheck.a $(AM_V_AR)$(libcheck_a_AR) libcheck.a $(libcheck_a_OBJECTS) $(libcheck_a_LIBADD) $(AM_V_at)$(RANLIB) libcheck.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_api.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_bfd.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_daemon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_data.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_dns.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_file.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_genhash.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_http.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_misc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_nftables.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_parser.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_ping.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_print.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_smtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_snmp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_ssl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_tcp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/check_udp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipvswrapper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipwrapper.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libipvs.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/check_api.Po -rm -f ./$(DEPDIR)/check_bfd.Po -rm -f ./$(DEPDIR)/check_daemon.Po -rm -f ./$(DEPDIR)/check_data.Po -rm -f ./$(DEPDIR)/check_dns.Po -rm -f ./$(DEPDIR)/check_file.Po -rm -f ./$(DEPDIR)/check_genhash.Po -rm -f ./$(DEPDIR)/check_http.Po -rm -f ./$(DEPDIR)/check_misc.Po -rm -f ./$(DEPDIR)/check_nftables.Po -rm -f ./$(DEPDIR)/check_parser.Po -rm -f ./$(DEPDIR)/check_ping.Po -rm -f ./$(DEPDIR)/check_print.Po -rm -f ./$(DEPDIR)/check_smtp.Po -rm -f ./$(DEPDIR)/check_snmp.Po -rm -f ./$(DEPDIR)/check_ssl.Po -rm -f ./$(DEPDIR)/check_tcp.Po -rm -f ./$(DEPDIR)/check_udp.Po -rm -f ./$(DEPDIR)/ipvswrapper.Po -rm -f ./$(DEPDIR)/ipwrapper.Po -rm -f ./$(DEPDIR)/libipvs.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/check_api.Po -rm -f ./$(DEPDIR)/check_bfd.Po -rm -f ./$(DEPDIR)/check_daemon.Po -rm -f ./$(DEPDIR)/check_data.Po -rm -f ./$(DEPDIR)/check_dns.Po -rm -f ./$(DEPDIR)/check_file.Po -rm -f ./$(DEPDIR)/check_genhash.Po -rm -f ./$(DEPDIR)/check_http.Po -rm -f ./$(DEPDIR)/check_misc.Po -rm -f ./$(DEPDIR)/check_nftables.Po -rm -f ./$(DEPDIR)/check_parser.Po -rm -f ./$(DEPDIR)/check_ping.Po -rm -f ./$(DEPDIR)/check_print.Po -rm -f ./$(DEPDIR)/check_smtp.Po -rm -f ./$(DEPDIR)/check_snmp.Po -rm -f ./$(DEPDIR)/check_ssl.Po -rm -f ./$(DEPDIR)/check_tcp.Po -rm -f ./$(DEPDIR)/check_udp.Po -rm -f ./$(DEPDIR)/ipvswrapper.Po -rm -f ./$(DEPDIR)/ipwrapper.Po -rm -f ./$(DEPDIR)/libipvs.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-generic clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/check/check_bfd.c0000664000175000017500000002211114661620303014533 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: BFD CHECK. Perform a system call to run an extra * system prog or script. * * Authors: Quentin Armitage * * 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, */ #include "config.h" #include #include #include #include "main.h" #include "check_api.h" #include "ipwrapper.h" #include "logger.h" #include "smtp.h" #include "parser.h" #include "global_data.h" #include "global_parser.h" #include "bfd.h" #include "bfd_event.h" #include "bfd_daemon.h" #include "bitops.h" #ifdef THREAD_DUMP #include "scheduler.h" #endif #include "check_parser.h" /* local data */ static thread_ref_t bfd_thread; static void bfd_check_thread(thread_ref_t); /* Configuration stream handling */ static void free_bfd_check(checker_t *checker) { bfd_checker_t *bfd_checker = checker->data; FREE(bfd_checker); FREE(checker); } static void dump_bfd_check(FILE *fp, const checker_t *checker) { const bfd_checker_t *bfd_checker = checker->data; conf_write(fp, " Keepalive method = BFD_CHECK"); conf_write(fp, " Name = %s", bfd_checker->bfd->bname); conf_write(fp, " Alpha is %s", checker->alpha ? "ON" : "OFF"); // conf_write(fp, " weight = %d", bfd_checker->weight); } /* real server on a BFD's list */ void free_checker_tracked_bfd_list(list_head_t *l) { cref_tracked_bfd_t *tbfd, *tbfd_tmp; list_for_each_entry_safe(tbfd, tbfd_tmp, l, e_list) FREE(tbfd); } void free_bfds_rs_list(list_head_t *l) { tracking_obj_t *top, *top_tmp; list_for_each_entry_safe(top, top_tmp, l, e_list) FREE(top); } static void dump_bfds_rs(FILE *fp, const checker_t *checker) { conf_write(fp, " %s", FMT_RS(checker->rs, checker->vs)); } void dump_bfds_rs_list(FILE *fp, const list_head_t *l) { tracking_obj_t *top; list_for_each_entry(top, l, e_list) dump_bfds_rs(fp, top->obj.checker); } static bool compare_bfd_check(const checker_t *old_c, checker_t *new_c) { const bfd_checker_t *old = old_c->data; const bfd_checker_t *new = new_c->data; if (strcmp(old->bfd->bname, new->bfd->bname)) return false; return true; } static checker_tracked_bfd_t * __attribute__ ((pure)) find_checker_tracked_bfd_by_name(char *name) { checker_tracked_bfd_t *cbfd; list_for_each_entry(cbfd, &check_data->track_bfds, e_list) { if (!strcmp(cbfd->bname, name)) return cbfd; } return NULL; } static const checker_funcs_t bfd_checker_funcs = { CHECKER_BFD, free_bfd_check, dump_bfd_check, compare_bfd_check, NULL }; static void bfd_check_handler(__attribute__((unused)) const vector_t *strvec) { bfd_checker_t *new_bfd_checker; PMALLOC(new_bfd_checker); INIT_LIST_HEAD(&new_bfd_checker->e_list); /* queue new checker */ queue_checker(&bfd_checker_funcs, NULL, new_bfd_checker, NULL, false); } static void bfd_name_handler(const vector_t *strvec) { checker_tracked_bfd_t *ctbfd; cref_tracked_bfd_t *tbfd; bfd_checker_t *bfdc; bool config_error = true; char *name; bfdc = current_checker->data; if (vector_size(strvec) >= 2) name = vector_slot(strvec, 1); if (vector_size(strvec) != 2) report_config_error(CONFIG_GENERAL_ERROR, "(%s) BFD_CHECK - No or too many names specified" " - skipping checker" , FMT_RS(current_checker->rs, current_checker->vs)); else if (!(ctbfd = find_checker_tracked_bfd_by_name(name))) report_config_error(CONFIG_GENERAL_ERROR, "(%s) BFD_CHECK - BFD %s not configured" , FMT_RS(current_checker->rs, current_checker->vs) , name); else if (bfdc->bfd) report_config_error(CONFIG_GENERAL_ERROR, "(%s) BFD_CHECK - BFD %s already specified as %s" , FMT_RS(current_checker->rs, current_checker->vs) , name , bfdc->bfd->bname); else if (strlen(name) >= BFD_INAME_MAX) report_config_error(CONFIG_GENERAL_ERROR, "(%s) BFD_CHECK - BFD name %s too long" , FMT_RS(current_checker->rs, current_checker->vs) , name); else config_error = false; /* Now check we are not already monitoring it */ if (!config_error) { list_for_each_entry(tbfd, ¤t_checker->rs->tracked_bfds, e_list) { if (ctbfd == tbfd->bfd) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) BFD_CHECK - RS already monitoring %s" , FMT_RS(current_checker->rs, current_checker->vs) , strvec_slot(strvec, 1)); config_error = true; break; } } } if (config_error) { dequeue_new_checker(); return; } bfdc->bfd = ctbfd; } static void bfd_alpha_handler(const vector_t *strvec) { int res; 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; } } else res = true; current_checker->alpha = res; } static void bfd_end_handler(void) { bfd_checker_t *bfdc; tracking_obj_t *top; cref_tracked_bfd_t *tbfd; bfdc = current_checker->data; if (!bfdc->bfd) { report_config_error(CONFIG_GENERAL_ERROR, "(%s) No name has been specified for BFD_CHECKER - skipping" , FMT_RS(current_checker->rs, current_checker->vs)); dequeue_new_checker(); return; } /* Add the bfd to the RS's list */ PMALLOC(tbfd); INIT_LIST_HEAD(&tbfd->e_list); tbfd->bfd = bfdc->bfd; list_add_tail(&tbfd->e_list, ¤t_checker->rs->tracked_bfds); /* Add the checker to the BFD */ PMALLOC(top); INIT_LIST_HEAD(&top->e_list); top->type = TRACK_CHECKER; top->obj.checker = current_checker; list_add_tail(&top->e_list, &bfdc->bfd->tracking_rs); } void install_bfd_check_keyword(void) { vpp_t check_ptr; install_keyword("BFD_CHECK", &bfd_check_handler); check_ptr = install_sublevel(VPP ¤t_checker); install_keyword("name", &bfd_name_handler); install_keyword("alpha", &bfd_alpha_handler); install_level_end_handler(&bfd_end_handler); install_sublevel_end(check_ptr); } static void bfd_check_handle_event(bfd_event_t * evt) { struct timeval cur_time; struct timeval timer_tmp; uint32_t delivery_time; checker_tracked_bfd_t *cbfd; tracking_obj_t *top; checker_t *checker; char message[80]; bool checker_was_up; bool rs_was_alive; if (__test_bit(LOG_DETAIL_BIT, &debug)) { cur_time = timer_now(); timersub(&cur_time, &evt->sent_time, &timer_tmp); delivery_time = timer_long(timer_tmp); log_message(LOG_INFO, "Received BFD event: instance %s is in" " state %s (delivered in %" PRIu32 " usec)", evt->iname, BFD_STATE_STR(evt->state), delivery_time); } list_for_each_entry(cbfd, &check_data->track_bfds, e_list) { if (strcmp(cbfd->bname, evt->iname)) continue; /* We can't assume the state of the bfd instance up state * matches the checker up state due to the potential of * alpha state for some checkers and not others */ list_for_each_entry(top, &cbfd->tracking_rs, e_list) { checker = top->obj.checker; if ((evt->state == BFD_STATE_UP) == checker->is_up && checker->has_run) continue; log_message(LOG_INFO, "BFD check of [%s] RS(%s) is %s" , evt->iname, FMT_RS(checker->rs, checker->vs), evt->state == BFD_STATE_UP ? "UP" : "DOWN"); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(evt->state == BFD_STATE_UP ? UP : DOWN, checker); if (checker->rs->smtp_alert && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails) && (evt->state == BFD_STATE_UP) != checker_was_up) { snprintf(message, sizeof(message), "=> BFD CHECK %s %s on service <=", evt->iname, evt->state == BFD_STATE_UP ? "succeeded" : "failed"); smtp_alert(SMTP_MSG_RS, checker, NULL, message); } } break; } } static void bfd_check_thread(thread_ref_t thread) { bfd_event_t evt; if (thread->type == THREAD_READ_ERROR) { thread_close_fd(thread); return; } bfd_thread = thread_add_read(master, bfd_check_thread, NULL, thread->u.f.fd, TIMER_NEVER, 0); if (thread->type != THREAD_READY_READ_FD) return; while (read(thread->u.f.fd, &evt, sizeof(bfd_event_t)) != -1) bfd_check_handle_event(&evt); } void start_bfd_monitoring(thread_master_t *thread_master) { thread_add_read(thread_master, bfd_check_thread, NULL, bfd_checker_event_pipe[0], TIMER_NEVER, 0); } void checker_bfd_dispatcher_release(void) { thread_cancel(bfd_thread); bfd_thread = NULL; } #ifdef THREAD_DUMP void register_check_bfd_addresses(void) { register_thread_address("bfd_check_thread", bfd_check_thread); } #endif keepalived-2.3.3/keepalived/check/check_daemon.c0000664000175000017500000005201714745724073015267 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Healthcheckrs child process handling. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include #include #ifdef _WITH_PROFILING_ #include #endif #ifdef THREAD_DUMP #ifdef _WITH_SNMP_ #include "snmp.h" #endif #include "scheduler.h" #include "smtp.h" #include "check_dns.h" #include "check_http.h" #include "check_misc.h" #include "check_smtp.h" #include "check_tcp.h" #include "check_udp.h" #endif #include "check_daemon.h" #include "check_parser.h" #include "ipwrapper.h" #include "check_ssl.h" #include "check_api.h" #include "check_ping.h" #include "check_file.h" #include "global_data.h" #include "pidfile.h" #include "signals.h" #include "process.h" #include "memory.h" #include "logger.h" #include "main.h" #include "parser.h" #include "bitops.h" #include "keepalived_netlink.h" #include "check_print.h" #ifdef _WITH_SNMP_CHECKER_ #include "check_snmp.h" #endif #include "utils.h" #ifdef _WITH_BFD_ #include "bfd_daemon.h" #include "check_bfd.h" #endif #include "timer.h" #include "track_file.h" #ifdef _WITH_TRACK_PROCESS_ #include "track_process.h" #endif #ifdef _USE_SYSTEMD_NOTIFY_ #include "systemd.h" #endif #ifndef _ONE_PROCESS_DEBUG_ #include "config_notify.h" #endif /* Global variables */ bool using_ha_suspend; /* local variables */ static const char *check_syslog_ident; #ifndef _ONE_PROCESS_DEBUG_ static bool two_phase_terminate; static timeval_t check_start_time; static unsigned check_next_restart_delay; #endif /* set fd ulimits */ static void set_checker_max_fds(void) { /* Allow for: * 0 stdin * 1 stdout * 2 strerr * 3 memcheck log (debugging) * 4 log file * 5 epoll * 6 timerfd * 7 signalfd * 8 bfd pipe * 9 closed * 10 closed * 11 FIFO * 12 closed * 13 passwd file * 14 Unix domain socket * 15 memfd for config * 16 eventfd for notifying load/reload complete * One per checker using UDP/TCP/PING * One per SMTP alert * qty 10 spare */ set_max_file_limit(17 + check_data->num_checker_fd_required + check_data->num_smtp_alert + 10); } static void lvs_notify_fifo_script_exit(__attribute__((unused)) thread_ref_t thread) { log_message(LOG_INFO, "lvs notify fifo script terminated"); } static void checker_dispatcher_release(void) { #ifdef _WITH_BFD_ checker_bfd_dispatcher_release(); #endif cancel_signal_read_thread(); cancel_kernel_netlink_threads(); } static bool checker_ipvs_syncd_needed(void) { #ifdef _WITH_VRRP_ if (global_data->lvs_syncd.vrrp_name) return false; #endif return !!global_data->lvs_syncd.ifname; } /* Daemon stop sequence */ static int checker_terminate_phase2(void) { /* Remove the notify fifo */ notify_fifo_close(&global_data->notify_fifo, &global_data->lvs_notify_fifo); #ifdef _WITH_SNMP_CHECKER_ if (global_data && global_data->enable_snmp_checker) check_snmp_agent_close(); #endif /* Destroy master thread */ checker_dispatcher_release(); thread_destroy_master(master); master = NULL; free_ssl(); set_ping_group_range(false); /* If we are running both master and backup, stop them now */ if (checker_ipvs_syncd_needed()) { ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, IPVS_MASTER, true); ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, IPVS_BACKUP, true); } ipvs_stop(); /* Stop daemon */ pidfile_rm(&checkers_pidfile); /* Clean data */ if (global_data) free_global_data(&global_data); if (check_data) free_check_data(&check_data); free_parent_mallocs_exit(); /* * Reached when terminate signal catched. * finally return to parent process. */ log_stopping(); #ifdef ENABLE_LOG_TO_FILE if (log_file_name) close_log_file(); #endif closelog(); #ifndef _MEM_CHECK_LOG_ FREE_CONST_PTR(check_syslog_ident); #else if (check_syslog_ident) free(no_const_char_p(check_syslog_ident)); /* malloc'd by make_syslog_ident() */ #endif close_std_fd(); return 0; } static void checker_shutdown_backstop_thread(thread_ref_t thread) { int count = 0; thread_ref_t t; /* Force terminate all script processes */ if (thread->master->child.rb_root.rb_node) script_killall(thread->master, SIGKILL, true); rb_for_each_entry_cached_const(t, &thread->master->child, n) count++; log_message(LOG_ERR, "backstop thread invoked: shutdown timer %srunning, child count %d", thread->master->shutdown_timer_running ? "" : "not ", count); if (thread->master->shutdown_timer_running) thread_add_timer_shutdown(thread->master, checker_shutdown_backstop_thread, NULL, TIMER_HZ / 10); else thread_add_terminate_event(thread->master); } static void checker_terminate_phase1(bool schedule_next_thread) { if (using_ha_suspend || __test_bit(LOG_ADDRESS_CHANGES, &debug)) kernel_netlink_close(); if (check_data) { /* Terminate all script processes */ if (master->child.rb_root.rb_node) script_killall(master, SIGTERM, true); /* Stop monitoring files */ if (!list_empty(&check_data->track_files)) stop_track_files(); /* Send shutdown messages */ if (!__test_bit(DONT_RELEASE_IPVS_BIT, &debug)) clear_services(); } if (schedule_next_thread) { /* If there are no child processes, we can terminate immediately, * otherwise add a thread to allow reasonable time for children to terminate */ if (master->child.rb_root.rb_node) { /* Add a backstop timer for the shutdown */ thread_add_timer_shutdown(master, checker_shutdown_backstop_thread, NULL, TIMER_HZ); } else thread_add_terminate_event(master); } } #ifndef _ONE_PROCESS_DEBUG_ static void start_checker_termination_thread(__attribute__((unused)) thread_ref_t thread) { /* This runs in the context of a thread */ two_phase_terminate = true; checker_terminate_phase1(true); } #endif /* Daemon stop sequence */ static void stop_check(int status) { if (__test_bit(CONFIG_TEST_BIT, &debug)) return; /* This runs in the main process, not in the context of a thread */ checker_terminate_phase1(false); checker_terminate_phase2(); /* unreachable */ exit(status); } static void set_effective_weights(void) { virtual_server_t *vs; real_server_t *rs; checker_t *checker; list_for_each_entry(vs, &check_data->vs, e_list) { list_for_each_entry(rs, &vs->rs, e_list) { rs->effective_weight = rs->iweight; list_for_each_entry(checker, &rs->checkers_list, rs_list) checker->rs->effective_weight += checker->cur_weight; } } } /* Daemon init sequence */ static void start_check(data_t *prev_global_data) { /* Parse configuration file */ if (reload) global_data = alloc_global_data(); check_data = alloc_check_data(); if (!check_data) { stop_check(KEEPALIVED_EXIT_FATAL); return; } init_data(conf_file, check_init_keywords, false); if (reload) init_global_data(global_data, prev_global_data, true); /* Update process name if necessary */ if ((!prev_global_data && // startup global_data->lvs_process_name) || (prev_global_data && // reload (!global_data->lvs_process_name != !prev_global_data->lvs_process_name || (global_data->lvs_process_name && strcmp(global_data->lvs_process_name, prev_global_data->lvs_process_name))))) set_process_name(global_data->lvs_process_name); /* fill 'vsg' members of the virtual_server_t structure. * We must do that after parsing config, because * vs and vsg declarations may appear in any order, * but we must do it before validate_check_config(). */ link_vsg_to_vs(); /* Post initializations */ if (!validate_check_config() #ifndef _ONE_PROCESS_DEBUG_ || (global_data->reload_check_config && get_config_status() != CONFIG_OK) #endif ) { stop_check(KEEPALIVED_EXIT_CONFIG); return; } /* If we are just testing the configuration, then we terminate now */ if (__test_bit(CONFIG_TEST_BIT, &debug)) return; /* Initialize sub-system if any virtual servers are configured */ if ((!list_empty(&check_data->vs) || (reload && !list_empty(&old_check_data->vs))) && ipvs_start() != IPVS_SUCCESS) { stop_check(KEEPALIVED_EXIT_FATAL); return; } /* Set LVS timeouts */ if (global_data->lvs_timeouts.tcp_timeout || global_data->lvs_timeouts.tcp_fin_timeout || global_data->lvs_timeouts.udp_timeout) ipvs_set_timeouts(&global_data->lvs_timeouts); else if (reload) ipvs_set_timeouts(NULL); /* If we are managing the sync daemon, then stop any * instances of it that may have been running if * we terminated abnormally */ if (checker_ipvs_syncd_needed() && (!reload || ipvs_syncd_changed(&prev_global_data->lvs_syncd, &global_data->lvs_syncd))) { ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, IPVS_MASTER, true); ipvs_syncd_cmd(IPVS_STOPDAEMON, NULL, IPVS_BACKUP, true); } if (checker_ipvs_syncd_needed()) { /* If we are running both master and backup, start them now */ if (global_data->lvs_syncd.syncid == PARAMETER_UNSET) global_data->lvs_syncd.syncid = 0; ipvs_syncd_cmd(IPVS_STARTDAEMON, &global_data->lvs_syncd, IPVS_MASTER_BACKUP, false); } /* Ensure we can open sufficient file descriptors */ set_checker_max_fds(); /* Create a notify FIFO if needed, and open it */ notify_fifo_open(&global_data->notify_fifo, &global_data->lvs_notify_fifo, lvs_notify_fifo_script_exit, "lvs_"); /* Get current active addresses, and start update process */ if (using_ha_suspend || __test_bit(LOG_ADDRESS_CHANGES, &debug)) { if (reload) kernel_netlink_set_recv_bufs(); kernel_netlink_init(); } else if (reload) kernel_netlink_close(); /* Remove any entries left over from previous invocation */ if (!reload && global_data->lvs_flush) ipvs_flush_cmd(); #ifdef _WITH_SNMP_CHECKER_ if (global_data->enable_snmp_checker) { if (reload) snmp_epoll_info(master); else check_snmp_agent_init(global_data->snmp_socket); } #endif /* SSL load static data & initialize common ctx context */ if (check_data->ssl_required && !init_ssl_ctx()) stop_check(KEEPALIVED_EXIT_FATAL); /* We can send SMTP messages from here so set the time */ set_time_now(); /* Set up the track files */ add_rs_to_track_files(); init_track_files(&check_data->track_files); /* Processing differential configuration parsing */ set_track_file_weights(); if (reload) clear_diff_services(); set_track_file_checkers_down(); set_effective_weights(); if (reload) check_new_rs_state(); /* Initialize IPVS topology */ if (!init_services()) stop_check(KEEPALIVED_EXIT_FATAL); #ifndef _ONE_PROCESS_DEBUG_ /* Notify parent config has been read if appropriate */ if (!__test_bit(CONFIG_TEST_BIT, &debug)) notify_config_read(); #endif /* Dump configuration */ if (__test_bit(DUMP_CONF_BIT, &debug)) dump_data_check(NULL); /* Register checkers thread */ register_checkers_thread(); /* Set the process priority and non swappable if configured */ set_process_priorities(global_data->checker_realtime_priority, global_data->max_auto_priority, global_data->min_auto_priority_delay, global_data->checker_rlimit_rt, global_data->checker_process_priority, global_data->checker_no_swap ? 4096 : 0); /* Set the process cpu affinity if configured */ set_process_cpu_affinity(&global_data->checker_cpu_mask, "checker"); } void check_validate_config(void) { start_check(NULL); } #ifndef _ONE_PROCESS_DEBUG_ /* Reload thread */ static void reload_check_thread(__attribute__((unused)) thread_ref_t thread) { bool with_snmp = false; log_message(LOG_INFO, "Reloading"); /* Use standard scheduling while reloading */ reset_process_priorities(); #ifndef _ONE_PROCESS_DEBUG_ save_config(false, "check", dump_data_check); #endif reinitialise_global_vars(); /* set the reloading flag */ SET_RELOAD; /* Terminate all script process */ script_killall(master, SIGTERM, false); if (!list_empty(&check_data->track_files)) stop_track_files(); /* Remove the notify fifo - we don't know if it will be the same after a reload */ notify_fifo_close(&global_data->notify_fifo, &global_data->lvs_notify_fifo); #if !defined _ONE_PROCESS_DEBUG_ && defined _WITH_SNMP_CHECKER_ if (prog_type == PROG_TYPE_CHECKER && global_data->enable_snmp_checker) with_snmp = true; #endif /* Destroy master thread */ checker_dispatcher_release(); thread_cleanup_master(master, true); thread_add_base_threads(master, with_snmp); free_ssl(); ipvs_stop(); /* Save previous conf data */ old_check_data = check_data; check_data = NULL; old_global_data = global_data; global_data = NULL; /* Reload the conf */ start_check(old_global_data); /* free backup data */ free_check_data(&old_check_data); free_global_data(&old_global_data); #ifndef _ONE_PROCESS_DEBUG_ save_config(true, "check", dump_data_check); #endif UNSET_RELOAD; #ifdef _MEM_CHECK_ log_message(LOG_INFO, "Configuration is using : %zu Bytes", get_keepalived_cur_mem_allocated()); #endif } static void print_check_data(__attribute__((unused)) thread_ref_t thread) { check_print_data(); } static void sigusr1_check(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { log_message(LOG_INFO, "Printing checker data for process(%d) on signal", getpid()); thread_add_event(master, print_check_data, NULL, 0); } static void sigreload_check(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { thread_add_event(master, reload_check_thread, NULL, 0); } /* Terminate handler */ static void sigend_check(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { if (master) thread_add_start_terminate_event(master, start_checker_termination_thread); } /* CHECK Child signal handling */ static void check_signal_init(void) { signal_set(SIGHUP, sigreload_check, NULL); if (ignore_sigint) signal_ignore(SIGINT); else signal_set(SIGINT, sigend_check, NULL); signal_set(SIGTERM, sigend_check, NULL); signal_set(SIGUSR1, sigusr1_check, NULL); #ifdef THREAD_DUMP signal_set(SIGTDUMP, thread_dump_signal, NULL); #endif signal_ignore(SIGPIPE); } /* This function runs in the parent process. */ static void delayed_restart_check_child_thread(__attribute__((unused)) thread_ref_t thread) { start_check_child(); } /* CHECK Child respawning thread. This function runs in the parent process. */ static void check_respawn_thread(thread_ref_t thread) { unsigned restart_delay; int ret; /* We catch a SIGCHLD, handle it */ checkers_child = 0; if ((ret = report_child_status(thread->u.c.status, thread->u.c.pid, NULL))) thread_add_parent_terminate_event(thread->master, ret); else if (!__test_bit(DONT_RESPAWN_BIT, &debug)) { log_child_died("Healthcheck", thread->u.c.pid); restart_delay = calc_restart_delay(&check_start_time, &check_next_restart_delay, "Healthcheck"); if (!restart_delay) start_check_child(); else thread_add_timer(thread->master, delayed_restart_check_child_thread, NULL, restart_delay * TIMER_HZ); } else { log_message(LOG_ALERT, "Healthcheck child process(%d) died: Exiting", thread->u.c.pid); raise(SIGTERM); } } #endif #ifdef THREAD_DUMP static void register_check_thread_addresses(void) { register_scheduler_addresses(); register_signal_thread_addresses(); register_notify_addresses(); register_smtp_addresses(); register_keepalived_netlink_addresses(); #ifdef _WITH_SNMP_ register_snmp_addresses(); #endif register_check_dns_addresses(); register_check_http_addresses(); register_check_misc_addresses(); register_check_smtp_addresses(); register_check_ssl_addresses(); register_check_tcp_addresses(); register_check_ping_addresses(); register_check_udp_addresses(); register_check_file_addresses(); #ifdef _WITH_BFD_ register_check_bfd_addresses(); #endif #ifndef _ONE_PROCESS_DEBUG_ register_thread_address("reload_check_thread", reload_check_thread); register_thread_address("start_checker_termination_thread", start_checker_termination_thread); register_thread_address("print_check_data", print_check_data); #endif register_thread_address("lvs_notify_fifo_script_exit", lvs_notify_fifo_script_exit); register_thread_address("checker_shutdown_backstop_thread", checker_shutdown_backstop_thread); #ifndef _ONE_PROCESS_DEBUG_ register_signal_handler_address("sigreload_check", sigreload_check); register_signal_handler_address("sigend_check", sigend_check); register_signal_handler_address("sigusr1_check", sigusr1_check); #ifdef THREAD_DUMP register_signal_handler_address("thread_dump_signal", thread_dump_signal); #endif #endif } #endif /* Register CHECK thread */ int start_check_child(void) { #ifndef _ONE_PROCESS_DEBUG_ pid_t pid; const char *syslog_ident; /* Initialize child process */ #ifdef ENABLE_LOG_TO_FILE if (log_file_name) flush_log_file(); #endif pid = fork(); if (pid < 0) { log_message(LOG_INFO, "Healthcheck child process: fork error(%s)" , strerror(errno)); return -1; } else if (pid) { checkers_child = pid; check_start_time = time_now; log_message(LOG_INFO, "Starting Healthcheck child process, pid=%d" , pid); /* Start respawning thread */ thread_add_child(master, check_respawn_thread, NULL, pid, TIMER_NEVER); return 0; } #ifdef _WITH_PROFILING_ /* See https://lists.gnu.org/archive/html/bug-gnu-utils/2001-09/msg00047.html for details */ monstartup ((u_long) &_start, (u_long) &etext); #endif prctl(PR_SET_PDEATHSIG, SIGTERM); /* Check our parent hasn't already changed since the fork */ if (main_pid != getppid()) kill(getpid(), SIGTERM); prog_type = PROG_TYPE_CHECKER; initialise_debug_options(); #ifdef THREAD_DUMP /* Remove anything we might have inherited from parent */ deregister_thread_addresses(); #endif close_other_pidfiles(); #ifdef _WITH_BFD_ /* Close the write end of the BFD checker event notification pipe and the track_process fd */ close(bfd_checker_event_pipe[1]); #ifdef _WITH_VRRP_ close(bfd_vrrp_event_pipe[0]); close(bfd_vrrp_event_pipe[1]); #endif #endif #ifdef _WITH_TRACK_PROCESS_ close_track_processes(); #endif if ((global_data->instance_name || global_data->network_namespace) && (check_syslog_ident = make_syslog_ident(PROG_CHECK))) syslog_ident = check_syslog_ident; else syslog_ident = PROG_CHECK; /* Opening local CHECK syslog channel */ if (!__test_bit(NO_SYSLOG_BIT, &debug)) open_syslog(syslog_ident); #ifdef ENABLE_LOG_TO_FILE if (log_file_name) open_log_file(log_file_name, "check", global_data->network_namespace, global_data->instance_name); #endif #ifdef _MEM_CHECK_ mem_log_init(PROG_CHECK, "Healthcheck child process"); #endif #ifdef _OPENSSL_MEM_CHECK_ if (__test_bit(OPENSSL_MEM_CHECK_BIT, &debug)) openssl_mem_log_init("OpenSSL_lib", "OpenSSL library"); #endif free_parent_mallocs_startup(true); /* Clear any child finder functions set in parent */ set_child_finder_name(NULL); /* Create an independant file descriptor for the shared config file */ separate_config_file(); /* Child process part, write pidfile */ if (!pidfile_write(&checkers_pidfile)) { log_message(LOG_INFO, "Healthcheck child process: cannot write pidfile"); exit(KEEPALIVED_EXIT_FATAL); } #ifdef _USE_SYSTEMD_NOTIFY_ systemd_unset_notify(); #endif /* Create the new master thread */ thread_destroy_master(master); /* This destroys any residual settings from the parent */ master = thread_make_master(); #endif /* If last process died during a reload, we can get there and we * don't want to loop again, because we're not reloading anymore. */ UNSET_RELOAD; #ifndef _ONE_PROCESS_DEBUG_ /* Signal handling initialization */ check_signal_init(); /* Register emergency shutdown function */ register_shutdown_function(stop_check); #endif /* Start Healthcheck daemon */ start_check(NULL); #ifdef _ONE_PROCESS_DEBUG_ return 0; #endif #ifdef THREAD_DUMP register_check_thread_addresses(); #endif #ifdef _MEM_CHECK_ log_message(LOG_INFO, "Configuration is using : %zu Bytes", get_keepalived_cur_mem_allocated()); #endif /* Launch the scheduling I/O multiplexer */ launch_thread_scheduler(master); /* Finish healthchecker daemon process */ #ifndef _ONE_PROCESS_DEBUG_ if (two_phase_terminate) checker_terminate_phase2(); else #endif stop_check(KEEPALIVED_EXIT_OK); #ifdef THREAD_DUMP deregister_thread_addresses(); #endif /* unreachable */ exit(KEEPALIVED_EXIT_OK); } #ifdef THREAD_DUMP void register_check_parent_addresses(void) { #ifndef _ONE_PROCESS_DEBUG_ register_thread_address("check_respawn_thread", check_respawn_thread); register_thread_address("delayed_restart_check_child_thread", delayed_restart_check_child_thread); #endif } #endif keepalived-2.3.3/keepalived/check/check_genhash.c0000664000175000017500000002247714661620303015434 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: WEB CHECK. Common HTTP/SSL checker primitives. * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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, */ /* * The hash generated is the same as the one you can get from * wget or curl: * wget http://[url]/[path] -O - | md5sum * curl http://[url]/[path] | md5sum */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "check_api.h" #include "check_http.h" #include "check_ssl.h" #include "check_genhash.h" #include "bitops.h" #include "logger.h" #include "parser.h" #include "utils.h" #include "signals.h" #include "scheduler.h" /* * Genhash utility */ static void genhash_usage(const char *prog, bool am_genhash) { fprintf(stderr, "Usage: %s%s COMMAND [OPTIONS]\n" "Commands:\n" " -s server-address -p port -u url\n" " -S -s server-address -p port -u url\n" " -h\n\n", prog, am_genhash ? "" : " --genhash"); fprintf(stderr, "Options:\n" "Either long or short options are allowed.\n" " --use-ssl -S Use SSL connection to remote server.\n" #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ " --use-sni -I Use SNI during SSL handshake (uses virtualhost setting; see -V).\n" #endif " --server -s Use the specified remote server address.\n" " --port -p Use the specified remote server port.\n" " --url -u Use the specified remote server url.\n" " --use-virtualhost -V Use the specified virtualhost in GET query.\n" " --verbose -v Use verbose mode output.\n" " --help -h Display this short inlined help screen.\n" " --fwmark -m Use the specified FW mark.\n" " --protocol -P Use the specified HTTP protocol - '1.0', 1.0c', '1.1'.\n" " 1.0c means 1.0 with 'Connection: close'\n" " --timeout -t Timeout in seconds\n"); } static int check_genhash_parse_cmdline(int argc, char **argv, checker_t *checker) { http_checker_t *http_get_check = checker->data; conn_opts_t *co = checker->co; const char *start; char *endptr; long port_num; url_t *url; unsigned long parsed_bits = 0; int c; bool bad_option = false; int curind; int longindex; struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {"use-ssl", no_argument, 0, 'S'}, #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ {"use-sni", no_argument, 0, 'I'}, #endif {"server", required_argument, 0, 's'}, {"use-virtualhost", required_argument, 0, 'V'}, {"port", required_argument, 0, 'p'}, {"url", required_argument, 0, 'u'}, {"fwmark", required_argument, 0, 'm'}, {"protocol", required_argument, 0, 'P'}, {"timeout", required_argument, 0, 't'}, {0, 0, 0, 0} }; /* Trivial sanity check */ if (argc <= 2) return -1; /* Parse the command line arguments */ curind = optind; while (longindex = -1, (c = getopt_long(argc, argv, ":hvSs:V:p:u:m:P:t:" #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ "I" #endif , long_options, &longindex)) != EOF) { switch (c) { case 'h': bad_option = true; break; case 'v': ((http_checker_t *)checker->data)->genhash_flags |= GENHASH_VERBOSE; break; case 'S': http_get_check->proto = PROTO_SSL; if (!init_ssl_ctx()) { fprintf(stderr, "Cannot initialize SSL context.\n"); return -1; } __set_bit(GENHASH_SSL_BIT, &parsed_bits); break; #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ case 'I': http_get_check->enable_sni = true; __set_bit(GENHASH_SNI_BIT, &parsed_bits); break; #endif case 's': if (inet_stosockaddr(optarg, NULL, &co->dst)) { fprintf(stderr, "server should be an IP, not %s\n", optarg); return -1; } __set_bit(GENHASH_SERVER_BIT, &parsed_bits); break; case 'V': RELAX_INLINE_START http_get_check->virtualhost = STRDUP(optarg); RELAX_INLINE_END __set_bit(GENHASH_VHOST_BIT, &parsed_bits); break; case 'p': port_num = strtol(optarg, &endptr, 10); if (*endptr || port_num <= 0 || port_num > 65535) { fprintf(stderr, "invalid port number '%s'\n", optarg); return -1; } checker_set_dst_port(&co->dst, htons(port_num)); __set_bit(GENHASH_PORT_BIT, &parsed_bits); break; case 'u': PMALLOC(url); INIT_LIST_HEAD(&url->e_list); RELAX_INLINE_START url->path = STRDUP(optarg); RELAX_INLINE_END url->digest = MALLOC(MD5_DIGEST_LENGTH); list_add_tail(&url->e_list, &http_get_check->url); http_get_check->url_it = url; __set_bit(GENHASH_URL_BIT, &parsed_bits); break; case 'm': #ifdef _WITH_SO_MARK_ start = optarg + strspn(optarg, " \t"); co->fwmark = (unsigned)strtoul(start, &endptr, 10); if (*endptr || start[0] == '-' || start[0] == ' ') { fprintf(stderr, "invalid fwmark '%s'\n", optarg); return -1; } __set_bit(GENHASH_FWMARK_BIT, &parsed_bits); #else fprintf(stderr, "keepalived built without fwmark support\n"); return -1; #endif break; case 'P': if (!strcmp(optarg, "1.0")) http_get_check->http_protocol = HTTP_PROTOCOL_1_0; else if (!strcmp(optarg, "1.0c") || !strcmp(optarg, "1.0C")) http_get_check->http_protocol = HTTP_PROTOCOL_1_0C; else if (!strcmp(optarg, "1.1")) http_get_check->http_protocol = HTTP_PROTOCOL_1_1; else { fprintf(stderr, "invalid HTTP protocol version '%s'\n", optarg); return -1; } __set_bit(GENHASH_PROTO_BIT, &parsed_bits); break; case 't': start = optarg + strspn(optarg, " \t"); co->connection_to = (unsigned)strtoul(start, &endptr, 10); if (*endptr || start[0] == '-' || !start[0]) { fprintf(stderr, "invalid timeout '%s'\n", optarg); return -1; } co->connection_to *= TIMER_HZ; __set_bit(GENHASH_TIMEOUT_BIT, &parsed_bits); break; case '?': if (optopt && argv[curind][1] != '-') fprintf(stderr, "Unknown option -%c\n", optopt); else fprintf(stderr, "Unknown option %s\n", argv[curind]); bad_option = true; break; case ':': if (optopt && argv[curind][1] != '-') fprintf(stderr, "Missing parameter for option -%c\n", optopt); else fprintf(stderr, "Missing parameter for option --%s\n", long_options[longindex].name); bad_option = true; break; default: fprintf(stderr, "Unknown option `-%c`\n", c); return -1; } curind = optind; } if (bad_option) return -1; /* check unexpected arguments */ if (optind < argc) { fprintf(stderr, "Unexpected argument(s):"); while (optind < argc) fprintf(stderr, " %s", argv[optind++]); fprintf(stderr, "\n"); return -1; } /* Mandatory options are: server, port & url */ return (__test_bit(GENHASH_SERVER_BIT, &parsed_bits) && __test_bit(GENHASH_PORT_BIT, &parsed_bits) && __test_bit(GENHASH_URL_BIT, &parsed_bits)) ? 0 : -1; } /* Terminate handler */ static void sigend(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { /* register the terminate thread */ thread_add_terminate_event(master); } void __attribute__ ((noreturn)) check_genhash(bool am_genhash, int argc, char **argv) { checker_t *checker; http_checker_t *http_get_check; virtual_server_t *vs; real_server_t *rs; conn_opts_t *co; int ret = 0; unsigned long ref_time = 0; /* Create a dummy checker */ PMALLOC(check_data); PMALLOC(checker); PMALLOC(vs); PMALLOC(rs); checker->vs = vs; checker->rs = rs; PMALLOC(co); co->connection_to = UINT_MAX; checker->co = co; PMALLOC(http_get_check); INIT_LIST_HEAD(&http_get_check->url); http_get_check->genhash_flags = GENHASH; http_get_check->proto = PROTO_HTTP; checker->data = http_get_check; checker->enabled = true; /* Parse command line */ if (check_genhash_parse_cmdline(argc, argv, checker) < 0) { genhash_usage(argv[0], am_genhash); ret = 1; goto end; } /* Submit work to I/O MUX */ master = thread_make_master(); signal_set(SIGINT, sigend, NULL); if (http_get_check->genhash_flags & GENHASH_VERBOSE) ref_time = timer_long(timer_now()); signal_set(SIGTERM, sigend, NULL); thread_add_event(master, http_connect_thread, checker, 0); launch_thread_scheduler(master); if (http_get_check->genhash_flags & GENHASH_VERBOSE) printf("\nGlobal response time for [%s] = %lu usecs\n", http_get_check->url_it->path, timer_long(timer_now()) - ref_time); /* Release memory */ thread_destroy_master(master); end: free_http_check(checker); FREE(vs); FREE(rs); FREE(check_data); exit(ret); } keepalived-2.3.3/keepalived/check/check_misc.c0000664000175000017500000004045614705542521014753 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: MISC CHECK. Perform a system call to run an extra * system prog or script. * * Authors: Alexandre Cassen, * Eric Jarman, * Bradley Baetz, * * 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, */ #include "config.h" #include #include #include #include "main.h" #include "check_misc.h" #include "check_api.h" #include "ipwrapper.h" #include "logger.h" #include "smtp.h" #include "utils.h" #include "parser.h" #include "daemon.h" #include "global_data.h" #include "global_parser.h" #include "keepalived_magic.h" #ifdef THREAD_DUMP #include "scheduler.h" #endif #include "check_parser.h" static void misc_check_thread(thread_ref_t); static void misc_check_child_thread(thread_ref_t); static bool script_user_set; /* Configuration stream handling */ static void free_misc_check(checker_t *checker) { misc_checker_t *misck_checker = checker->data; notify_free_script(&misck_checker->script); FREE(misck_checker); FREE(checker); } static void dump_misc_check(FILE *fp, const checker_t *checker) { const misc_checker_t *misck_checker = checker->data; char time_str[26]; conf_write(fp, " Keepalive method = MISC_CHECK"); if (misck_checker->script.path) conf_write(fp, " path = %s", misck_checker->script.path); conf_write(fp, " script = %s", cmd_str(&misck_checker->script)); conf_write(fp, " timeout = %lu", misck_checker->timeout/TIMER_HZ); conf_write(fp, " dynamic = %s", misck_checker->dynamic ? "YES" : "NO"); conf_write(fp, " uid:gid = %u:%u", misck_checker->script.uid, misck_checker->script.gid); ctime_r(&misck_checker->last_ran.tv_sec, time_str); conf_write(fp, " Last ran = %" PRI_tv_sec ".%6.6" PRI_tv_usec " (%.24s.%6.6" PRI_tv_usec ")", misck_checker->last_ran.tv_sec, misck_checker->last_ran.tv_usec, time_str, misck_checker->last_ran.tv_usec); conf_write(fp, " Last status = %u", misck_checker->last_exit_code); } static bool compare_misc_check(const checker_t *old_c, checker_t *new_c) { const misc_checker_t *old = old_c->data; const misc_checker_t *new = new_c->data; if (new->dynamic != old->dynamic) return false; return notify_script_compare(&old->script, &new->script); } static void migrate_misc_check(checker_t *new_c, const checker_t *old_c) { const misc_checker_t *old = old_c->data; misc_checker_t *new = new_c->data; new->last_exit_code = old->last_exit_code; new->last_ran = old->last_ran; if (!new->dynamic || old->last_exit_code == 1) return; new_c->cur_weight = new->last_exit_code - (new->last_exit_code ? 2 : 0) - new_c->rs->iweight; } static const checker_funcs_t misc_checker_funcs = { CHECKER_MISC, free_misc_check, dump_misc_check, compare_misc_check, migrate_misc_check }; static void misc_check_handler(__attribute__((unused)) const vector_t *strvec) { misc_checker_t *new_misck_checker; PMALLOC(new_misck_checker); new_misck_checker->state = SCRIPT_STATE_IDLE; script_user_set = false; /* queue new checker */ queue_checker(&misc_checker_funcs, misc_check_thread, new_misck_checker, NULL, false); /* Set non-standard default value */ current_checker->default_retry = 0; } static void misc_path_handler(__attribute__((unused)) const vector_t *strvec) { const vector_t *strvec_qe; misc_checker_t *new_misck_checker = current_checker->data; /* We need to allow quoted and escaped strings for the script and parameters */ strvec_qe = alloc_strvec_quoted(NULL); set_script_params_array(strvec_qe, &new_misck_checker->script, 0); free_strvec(strvec_qe); } static void misc_timeout_handler(const vector_t *strvec) { unsigned timeout; misc_checker_t *new_misck_checker = current_checker->data; if (!read_unsigned_strvec(strvec, 1, &timeout, 0, UINT_MAX / TIMER_HZ, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid misc_timeout value '%s'", strvec_slot(strvec, 1)); return; } new_misck_checker->timeout = timeout * TIMER_HZ; } static void misc_dynamic_handler(__attribute__((unused)) const vector_t *strvec) { misc_checker_t *new_misck_checker = current_checker->data; new_misck_checker->dynamic = true; } static void misc_user_handler(const vector_t *strvec) { misc_checker_t *new_misck_checker = current_checker->data; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No user specified for misc checker script %s", cmd_str(&new_misck_checker->script)); return; } if (set_script_uid_gid(strvec, 1, &new_misck_checker->script.uid, &new_misck_checker->script.gid)) { report_config_error(CONFIG_GENERAL_ERROR, "Failed to set uid/gid for misc checker script %s - removing", cmd_str(&new_misck_checker->script)); dequeue_new_checker(); } else script_user_set = true; } static void misc_end_handler(void) { misc_checker_t *new_misck_checker = current_checker->data; if (!new_misck_checker->script.args) { report_config_error(CONFIG_GENERAL_ERROR, "No script path has been specified for MISC_CHECKER - skipping"); dequeue_new_checker(); return; } if (!script_user_set) { if (get_default_script_user(&new_misck_checker->script.uid, &new_misck_checker->script.gid)) { report_config_error(CONFIG_GENERAL_ERROR, "Unable to set default user for misc script %s - removing", cmd_str(&new_misck_checker->script)); dequeue_new_checker(); return; } } } void install_misc_check_keyword(void) { vpp_t check_ptr; install_keyword("MISC_CHECK", &misc_check_handler); check_ptr = install_sublevel(VPP ¤t_checker); install_checker_common_keywords(false); install_keyword_quoted("misc_path", &misc_path_handler); install_keyword("misc_timeout", &misc_timeout_handler); install_keyword("misc_dynamic", &misc_dynamic_handler); install_keyword("user", &misc_user_handler); install_level_end_handler(&misc_end_handler); install_sublevel_end(check_ptr); } /* Check that the scripts are secure */ unsigned check_misc_script_security(magic_t magic) { virtual_server_t *vs; real_server_t *rs; checker_t *checker, *checker_tmp; misc_checker_t *misc_script; unsigned script_flags = 0; unsigned flags; bool insecure; list_for_each_entry(vs, &check_data->vs, e_list) { list_for_each_entry(rs, &vs->rs, e_list) { list_for_each_entry_safe(checker, checker_tmp, &rs->checkers_list, rs_list) { if (checker->launch != misc_check_thread) continue; misc_script = CHECKER_ARG(checker); script_flags |= (flags = check_script_secure(&misc_script->script, magic)); /* Mark not to run if needs inhibiting */ insecure = false; if (flags & SC_INHIBIT) { log_message(LOG_INFO, "Disabling misc script %s due to insecure", cmd_str(&misc_script->script)); insecure = true; } else if (flags & SC_NOTFOUND) { log_message(LOG_INFO, "Disabling misc script %s since not found/accessible", cmd_str(&misc_script->script)); insecure = true; } else if (!(flags & (SC_EXECUTABLE | SC_SYSTEM))) insecure = true; if (insecure) { /* Remove the script */ free_checker(checker); } } } } return script_flags; } static void misc_check_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); misc_checker_t *misck_checker; int ret; misck_checker = CHECKER_ARG(checker); /* * Register a new checker thread & return * if checker is disabled */ if (!checker->enabled) { /* Register next timer checker */ thread_add_timer(thread->master, misc_check_thread, checker, checker->delay_loop); return; } /* Execute the script in a child process. Parent returns, child doesn't */ ret = system_call_script(thread->master, misc_check_child_thread, checker, (misck_checker->timeout) ? misck_checker->timeout : checker->vs->delay_loop, &misck_checker->script); if (!ret) { misck_checker->last_ran = time_now; misck_checker->state = SCRIPT_STATE_RUNNING; } } static void misc_check_child_thread(thread_ref_t thread) { int wait_status; pid_t pid; checker_t *checker; misc_checker_t *misck_checker; timeval_t next_time; int sig_num; unsigned timeout = 0; const char *script_exit_type = NULL; bool script_success = false; const char *reason = NULL; int reason_code = 0; /* Avoid uninitialised warning by older versions of gcc */ bool rs_was_alive; bool message_only = false; checker = THREAD_ARG(thread); misck_checker = CHECKER_ARG(checker); if (thread->type == THREAD_CHILD_TIMEOUT) { pid = THREAD_CHILD_PID(thread); if (misck_checker->state == SCRIPT_STATE_RUNNING) { misck_checker->state = SCRIPT_STATE_REQUESTING_TERMINATION; sig_num = SIGTERM; timeout = 2; } else if (misck_checker->state == SCRIPT_STATE_REQUESTING_TERMINATION) { misck_checker->state = SCRIPT_STATE_FORCING_TERMINATION; sig_num = SIGKILL; timeout = 2; } else if (misck_checker->state == SCRIPT_STATE_FORCING_TERMINATION) { log_message(LOG_INFO, "Child (PID %d) failed to terminate after kill", pid); sig_num = SIGKILL; timeout = 10; /* Give it longer to terminate */ } if (timeout) { /* If kill returns an error, we can't kill the process since either the process has terminated, * or we don't have permission. If we can't kill it, there is no point trying again. */ if (kill(-pid, sig_num)) { if (errno == ESRCH) { /* The process does not exist, and we should * have reaped its exit status, otherwise it * would exist as a zombie process. */ log_message(LOG_INFO, "Misc script %s child (PID %d) lost, register checker again", misck_checker->script.args[0], pid); misck_checker->state = SCRIPT_STATE_IDLE; timeout = 0; goto recheck; } else { log_message(LOG_INFO, "kill -%d of process %s(%d) with new state %u failed with errno %d", sig_num, misck_checker->script.args[0], pid, misck_checker->state, errno); timeout = 1000; } } } else if (misck_checker->state != SCRIPT_STATE_IDLE) { log_message(LOG_INFO, "Child thread pid %d timeout with unknown script state %u", pid, misck_checker->state); timeout = 10; /* We need some timeout */ } if (timeout) thread_add_child(thread->master, misc_check_child_thread, checker, pid, timeout * TIMER_HZ); return; } wait_status = THREAD_CHILD_STATUS(thread); if (WIFEXITED(wait_status)) { unsigned status = WEXITSTATUS(wait_status); int64_t effective_weight; if (status == 0 || (misck_checker->dynamic && status >= 2 && status <= 255)) { /* * The actual weight set when using misc_dynamic is two less than * the exit status returned. Effective range is 0..253. * Catch legacy case of status being 0 but misc_dynamic being set. */ if (status != misck_checker->last_exit_code) { effective_weight = checker->rs->effective_weight - checker->cur_weight; checker->cur_weight = status ? status - 2 - checker->rs->iweight : 0; effective_weight += checker->cur_weight; update_svr_wgt(effective_weight, checker->vs, checker->rs, true); misck_checker->last_exit_code = status; } /* everything is good */ if (!checker->is_up || !checker->has_run) { script_exit_type = "succeeded"; script_success = true; } checker->retry_it = 0; } else if (checker->is_up || !checker->has_run) { script_exit_type = "failed"; reason = "exited with status"; reason_code = status; if (checker->retry_it < checker->retry) { checker->retry_it++; if (global_data->checker_log_all_failures || checker->log_all_failures) message_only = true; else script_exit_type = NULL; /* this disables all message handling */ } else { checker->retry_it = 0; checker->rs->effective_weight -= checker->cur_weight; checker->cur_weight = 0; misck_checker->last_exit_code = status; } } else // if (status != misck_checker->last_exit_code) misck_checker->last_exit_code = status; } else if (WIFSIGNALED(wait_status)) { if (misck_checker->state == SCRIPT_STATE_REQUESTING_TERMINATION && WTERMSIG(wait_status) == SIGTERM) { /* The script terminated due to a SIGTERM, and we sent it a SIGTERM to * terminate the process. Now make sure any children it created have * died too. */ pid = THREAD_CHILD_PID(thread); kill(-pid, SIGKILL); } /* We treat forced termination as a failure */ if (checker->is_up || !checker->has_run) { if ((misck_checker->state == SCRIPT_STATE_REQUESTING_TERMINATION && WTERMSIG(wait_status) == SIGTERM) || (misck_checker->state == SCRIPT_STATE_FORCING_TERMINATION && (WTERMSIG(wait_status) == SIGTERM || WTERMSIG(wait_status) == SIGKILL))) script_exit_type = "timed out"; else { script_exit_type = "failed"; reason = "due to signal"; reason_code = WTERMSIG(wait_status); } if (checker->retry_it < checker->retry) { checker->retry_it++; if (global_data->checker_log_all_failures || checker->log_all_failures) message_only = true; else script_exit_type = NULL; /* this disables all message handling */ } else checker->retry_it = 0; } } if (script_exit_type) { /* not the case if retry check will follow and log_all_failures unset */ char message[40]; if (!script_success) { /* * retry is always the fixed value of config parameter retry * If retry > 0 then on the regulary scheduled check fail retry_it is 1, on the first retry check fail retry_it is 2 and so on * but on the last retry check fail, retry_it is 0 */ if (checker->retry) { if (checker->retry_it == 1) /* failed regulary scheduled check */ snprintf(message, sizeof(message), ", will do %u %s", checker->retry, checker->retry == 1 ? "retry" : "retries"); else if (checker->retry_it > 1) /* failed retry check, but not the last */ snprintf(message, sizeof(message), " on retry %u/%u", checker->retry_it - 1, checker->retry); else /* failed last retry check */ snprintf(message, sizeof(message), " after %u %s", checker->retry, checker->retry == 1 ? "retry" : "retries" ); } else /* retry 0 */ snprintf(message, sizeof(message), " with retry disabled"); } else message[0] = '\0'; if (reason) log_message(LOG_INFO, "Misc check for [%s VS %s] by [%s] %s%s (%s %d)." , FMT_CHK(checker) , FMT_VS(checker->vs) , misck_checker->script.args[0] , script_exit_type , message , reason , reason_code); else log_message(LOG_INFO, "Misc check for [%s VS %s] by [%s] %s%s." , FMT_CHK(checker) , FMT_VS(checker->vs) , misck_checker->script.args[0] , script_exit_type , message); if (!message_only) { rs_was_alive = checker->rs->alive; update_svr_checker_state(script_success ? UP : DOWN, checker); if (!script_success) checker->cur_weight = 0; if (checker->rs->smtp_alert && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) { snprintf(message, sizeof(message), "=> MISC CHECK %s on service <=", script_exit_type); smtp_alert(SMTP_MSG_RS, checker, NULL, message); } } } recheck: /* Register next timer checker */ next_time = timer_add_long(misck_checker->last_ran, checker->retry_it ? checker->delay_before_retry : checker->delay_loop); next_time = timer_sub_now(next_time); if (next_time.tv_sec < 0 || (next_time.tv_sec == 0 && next_time.tv_usec == 0)) next_time.tv_sec = 0, next_time.tv_usec = 1; thread_add_timer(thread->master, misc_check_thread, checker, timer_long(next_time)); misck_checker->state = SCRIPT_STATE_IDLE; checker->has_run = true; } #ifdef THREAD_DUMP void register_check_misc_addresses(void) { register_thread_address("misc_check_child_thread", misc_check_child_thread); register_thread_address("misc_check_thread", misc_check_thread); } #endif keepalived-2.3.3/keepalived/check/check_data.c0000664000175000017500000013005714706175715014736 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Healthcheckers dynamic data structure definition. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include "main.h" #include "check_data.h" #include "check_parser.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" #include "track_file.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_data; PMALLOC(ssl_data); return ssl_data; } void free_ssl(void) { ssl_data_t *ssl; if (!check_data || !check_data->ssl) return; ssl = check_data->ssl; clear_ssl(ssl); FREE_CONST_PTR(ssl->password); FREE_CONST_PTR(ssl->cafile); FREE_CONST_PTR(ssl->certfile); FREE_CONST_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); /* lgtm[security] lgtm[external/cwe/cwe-313] the password is read from a file */ 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_entry_list(list_head_t *l) { virtual_server_group_entry_t *vsge, *vsge_tmp; list_for_each_entry_safe(vsge, vsge_tmp, l, e_list) { list_del_init(&vsge->e_list); FREE(vsge); } } static void dump_vsg_entry(FILE *fp, const virtual_server_group_entry_t *vsg_entry) { char start_addr[INET6_ADDRSTRLEN]; if (vsg_entry->is_fwmark) { conf_write(fp, " FWMARK = %u%s", vsg_entry->vfwmark, vsg_entry->fwm_family == AF_INET ? " IPv4" : vsg_entry->fwm_family == AF_INET6 ? " IPv6" : ""); conf_write(fp, " Alive: %u IPv4, %u IPv6", vsg_entry->fwm4_alive, vsg_entry->fwm6_alive); } else { if (inet_sockaddrcmp(&vsg_entry->addr, &vsg_entry->addr_end)) { strcpy(start_addr, inet_sockaddrtos(&vsg_entry->addr)); conf_write(fp, " VIP Range = %s-%s, VPORT = %d", start_addr, inet_sockaddrtos(&vsg_entry->addr_end), 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))); conf_write(fp, " Alive: %u tcp, %u udp, %u sctp", vsg_entry->tcp_alive, vsg_entry->udp_alive, vsg_entry->sctp_alive); } conf_write(fp, " reloaded = %s", vsg_entry->reloaded ? "True" : "False"); } static void dump_vsg_entry_list(FILE *fp, const list_head_t *l) { virtual_server_group_entry_t *vsge; list_for_each_entry(vsge, l, e_list) dump_vsg_entry(fp, vsge); } void free_vsg(virtual_server_group_t *vsg) { list_del_init(&vsg->e_list); FREE_PTR(vsg->gname); free_vsg_entry_list(&vsg->addr_range); free_vsg_entry_list(&vsg->vfwmark); FREE(vsg); } static void free_vsg_list(list_head_t *l) { virtual_server_group_t *vsg, *vsg_tmp; list_for_each_entry_safe(vsg, vsg_tmp, l, e_list) free_vsg(vsg); } static void dump_vsg(FILE *fp, const virtual_server_group_t *vsg) { conf_write(fp, " ------< Virtual server group >------"); conf_write(fp, " Virtual Server Group = %s, IPv4 = %s, IPv6 = %s", vsg->gname, vsg->have_ipv4 ? "yes" : "no", vsg->have_ipv6 ? "yes" : "no"); #ifdef _WITH_NFTABLES_ if (global_data->ipvs_nf_table_name && (vsg->auto_fwmark[TCP_INDEX] || vsg->auto_fwmark[UDP_INDEX] || vsg->auto_fwmark[SCTP_INDEX])) conf_write(fp, " Fwmark TCP: %u UDP: %u SCTP: %u", vsg->auto_fwmark[TCP_INDEX], vsg->auto_fwmark[UDP_INDEX], vsg->auto_fwmark[SCTP_INDEX]); #endif dump_vsg_entry_list(fp, &vsg->addr_range); dump_vsg_entry_list(fp, &vsg->vfwmark); } static void dump_vsg_list(FILE *fp, const list_head_t *l) { virtual_server_group_t *vsg; list_for_each_entry(vsg, l, e_list) dump_vsg(fp, vsg); } virtual_server_group_t * alloc_vsg(const char *gname) { virtual_server_group_t *new; PMALLOC(new); INIT_LIST_HEAD(&new->e_list); INIT_LIST_HEAD(&new->addr_range); INIT_LIST_HEAD(&new->vfwmark); new->gname = STRDUP(gname); return new; } void alloc_vsg_entry(const vector_t *strvec) { virtual_server_group_entry_t *new; uint32_t start; const char *port_str; uint32_t range; unsigned fwmark; const char *family_str; const char *addr_str = strvec_slot(strvec, 0); char *endptr; const char *mask_str; unsigned mask; uint32_t mask_bit, mask_bits; bool bad; unsigned i; const char *end_str; int diff; PMALLOC(new); INIT_LIST_HEAD(&new->e_list); if (!strcmp(addr_str, "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", current_vsg->gname, strvec_slot(strvec, 1), UINT32_MAX); FREE(new); return; } if (vector_size(strvec) > 2) { family_str = strvec_slot(strvec, 2); if (!strcmp(family_str, "inet")) { new->fwm_family = AF_INET; current_vsg->have_ipv4 = true; } else if (!strcmp(family_str, "inet6")) { new->fwm_family = AF_INET6; current_vsg->have_ipv6 = true; } else { report_config_error(CONFIG_GENERAL_ERROR, "(%s): fwmark '%u' family %s unknown - ignoring", current_vsg->gname, fwmark, family_str); FREE(new); return; } } else { new->fwm_family = AF_UNSPEC; current_vsg->fwmark_no_family = true; } new->vfwmark = fwmark; new->is_fwmark = true; list_add_tail(&new->e_list, ¤t_vsg->vfwmark); } else { 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(addr_str, port_str, &new->addr)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid virtual server group IP address %s %s%s%s - skipping", strvec_slot(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", addr_str); FREE(new); return; } #endif if ((mask_str = strchr(addr_str, '/'))) { mask = strtoul(mask_str + 1, &endptr, 10); if (*endptr || !mask || (new->addr.ss_family == AF_INET && mask > 32) || (new->addr.ss_family == AF_INET6 && mask > 128)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid netmask - %s - skipping", addr_str); FREE(new); return; } new->addr_end = new->addr; bad = false; if (new->addr.ss_family == AF_INET && mask < 32) { for (i = mask, mask_bit = 1, mask_bits = 0; i < 32; i++, mask_bit <<= 1) mask_bits |= mask_bit; if (PTR_CAST(struct sockaddr_in, &new->addr)->sin_addr.s_addr & htonl(mask_bits)) bad = true; else PTR_CAST(struct sockaddr_in, &new->addr_end)->sin_addr.s_addr |= htonl(mask_bits); } else if (mask < 128) { for (i = mask % 16, mask_bit = 1, mask_bits = 0; i < 16; i++, mask_bit <<= 1) mask_bits |= mask_bit; i = mask / 16; if (PTR_CAST(struct sockaddr_in6, &new->addr)->sin6_addr.s6_addr16[i] & htons(mask_bits)) bad = true; else { PTR_CAST(struct sockaddr_in6, &new->addr_end)->sin6_addr.s6_addr16[i] |= htons(mask_bits); for (i++; i < 8; i++) { if (PTR_CAST(struct sockaddr_in6, &new->addr)->sin6_addr.s6_addr16[i]) bad = true; else PTR_CAST(struct sockaddr_in6, &new->addr_end)->sin6_addr.s6_addr16[i] = 0xffff; } } } if (bad) { report_config_error(CONFIG_GENERAL_ERROR, "Address mask bits not empty - %s - skipping", addr_str); FREE(new); return; } } else { if ((end_str = strchr(addr_str, '-')) && ((new->addr.ss_family == AF_INET && strchr(end_str + 1, '.')) || (new->addr.ss_family == AF_INET6 && strchr(end_str + 1, ':')))) { if (inet_stosockaddr(++end_str, port_str, &new->addr_end)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid range end %s - skipping", addr_str); FREE(new); return; } if (new->addr.ss_family != new->addr_end.ss_family) { report_config_error(CONFIG_GENERAL_ERROR, "Range address families do not match %s - skipping", addr_str); FREE(new); return; } bad = false; if (new->addr.ss_family == AF_INET) { if (htonl(PTR_CAST(struct sockaddr_in, &new->addr)->sin_addr.s_addr) > htonl(PTR_CAST(struct sockaddr_in, &new->addr_end)->sin_addr.s_addr)) bad = true; } else { for (i = 0; i < 8; i++) { diff = htons(PTR_CAST(struct sockaddr_in6, &new->addr_end)->sin6_addr.s6_addr16[i]) - htons(PTR_CAST(struct sockaddr_in6, &new->addr)->sin6_addr.s6_addr16[i]); if (diff < 0) { bad = true; break; } if (diff > 0) break; } } if (bad) { report_config_error(CONFIG_GENERAL_ERROR, "Address range end is less than address range start - %s - skipping", addr_str); FREE(new); return; } } else { if (!inet_stor(addr_str, &range)) { FREE(new); return; } /* If no range specified, range == UINT32_MAX */ new->addr_end = new->addr; if (range != UINT32_MAX) { if (new->addr.ss_family == AF_INET) { PTR_CAST(struct sockaddr_in, &new->addr_end)->sin_addr.s_addr &= htonl(~0xFF); PTR_CAST(struct sockaddr_in, &new->addr_end)->sin_addr.s_addr |= htonl(range); start = ntohl(PTR_CAST(struct sockaddr_in, &new->addr)->sin_addr.s_addr) & 0xFF; } else { PTR_CAST(struct sockaddr_in6, &new->addr_end)->sin6_addr.s6_addr16[7] = htons(range); start = ntohs(PTR_CAST(struct sockaddr_in6, &new->addr)->sin6_addr.s6_addr16[7]); } if (start >= range) { report_config_error(CONFIG_GENERAL_ERROR, "Address range end is not greater than address range start - %s - skipping", addr_str); FREE(new); return; } } } } new->is_fwmark = false; list_add_tail(&new->e_list, ¤t_vsg->addr_range); if (new->addr.ss_family == AF_INET) current_vsg->have_ipv4 = true; else current_vsg->have_ipv6 = true; } } static void dump_forwarding_method(FILE *fp, const char *prefix, const real_server_t *rs) { const char *fwd_method = "forwarding method = "; #ifdef _HAVE_IPVS_TUN_TYPE_ const char *csum_str = ""; const char *tun_type = "TUN, type = "; #endif switch (rs->forwarding_method) { case IP_VS_CONN_F_MASQ: conf_write(fp, " %s%sNAT", prefix, fwd_method); break; case IP_VS_CONN_F_DROUTE: conf_write(fp, " %s%sDR", prefix, fwd_method); break; case IP_VS_CONN_F_TUNNEL: #ifdef _HAVE_IPVS_TUN_TYPE_ if (rs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_IPIP) conf_write(fp, " %s%s%sIPIP", prefix, fwd_method, tun_type); else { #ifdef _HAVE_IPVS_TUN_CSUM_ csum_str = rs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM ? ", no checksum" : rs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_CSUM ? ", checksum" : rs->tun_flags == IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM ? ", remote checksum" : ", unknown checksum type"; #endif if (rs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) conf_write(fp, " %s%sGUE, port = %u%s", fwd_method, tun_type, ntohs(rs->tun_port), csum_str); #ifdef _HAVE_IPVS_TUN_GRE_ else if (rs->tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE) conf_write(fp, " %s%sGRE%s", fwd_method, tun_type, csum_str); #endif } #else conf_write(fp, " %s%sTUN", prefix, fwd_method); #endif break; default: conf_write(fp, " %s 0x%x", fwd_method, rs->forwarding_method); break; } } /* * Real server facility functions */ void free_rs(real_server_t *rs) { list_del_init(&rs->e_list); free_notify_script(&rs->notify_up); free_notify_script(&rs->notify_down); free_track_file_monitor_list(&rs->track_files); #ifdef _WITH_BFD_ free_checker_tracked_bfd_list(&rs->tracked_bfds); #endif FREE_CONST_PTR(rs->virtualhost); #ifdef _WITH_SNMP_CHECKER_ FREE_CONST_PTR(rs->snmp_name); #endif free_rs_checkers(rs); FREE(rs); } static void free_rs_list(list_head_t *l) { real_server_t *rs, *rs_tmp; list_for_each_entry_safe(rs, rs_tmp, l, e_list) free_rs(rs); } void dump_tracking_rs(FILE *fp, const void *data) { const tracking_obj_t *top = PTR_CAST_CONST(tracking_obj_t, data); const checker_t *checker = top->obj.checker; conf_write(fp, " %s -> %s, weight %d%s", FMT_VS(checker->vs), FMT_RS(checker->rs, checker->vs), top->weight, top->weight_multiplier == -1 ? " reverse" : ""); } static void dump_notify_vs_rs_script(FILE *fp, const notify_script_t *script, const char *type, const char *state) { if (script->path) conf_write(fp, " %s %s notify script = %s, params = %s, uid:gid %u:%u", type, state, script->path, cmd_str(script), script->uid, script->gid); else conf_write(fp, " %s %s notify script = %s, uid:gid %u:%u", type, state, cmd_str(script), script->uid, script->gid); } static void dump_rs(FILE *fp, const real_server_t *rs) { #ifdef _WITH_BFD_ cref_tracked_bfd_t *tbfd; #endif conf_write(fp, " ------< Real server >------"); conf_write(fp, " RIP = %s, RPORT = %d, WEIGHT = %d EFF WEIGHT = %" PRIi64 , inet_sockaddrtos(&rs->addr) , ntohs(inet_sockaddrport(&rs->addr)) , real_weight(rs->effective_weight), rs->effective_weight); dump_forwarding_method(fp, "", rs); conf_write(fp, " Alpha is %s", rs->alpha ? "ON" : "OFF"); conf_write(fp, " connection timeout = %s", format_decimal(rs->connection_to, TIMER_HZ_DIGITS)); conf_write(fp, " connection limit range = %" PRIu32 " -> %" PRIu32, rs->l_threshold, rs->u_threshold); conf_write(fp, " Delay loop = %s" , format_decimal(rs->delay_loop, TIMER_HZ_DIGITS)); 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 = %s" , format_decimal(rs->delay_before_retry, TIMER_HZ_DIGITS)); if (rs->warmup != ULONG_MAX) conf_write(fp, " Warmup = %s", format_decimal(rs->warmup, TIMER_HZ_DIGITS)); conf_write(fp, " Inhibit on failure is %s", rs->inhibit ? "ON" : "OFF"); if (rs->notify_up) dump_notify_vs_rs_script(fp, rs->notify_up, "RS", "up"); if (rs->notify_down) dump_notify_vs_rs_script(fp, rs->notify_down, "RS", "down"); if (rs->virtualhost) conf_write(fp, " VirtualHost = '%s'", rs->virtualhost); #ifdef _WITH_SNMP_CHECKER_ if (rs->snmp_name) conf_write(fp, " SNMP name = %s", rs->snmp_name); #endif conf_write(fp, " Using smtp notification = %s", rs->smtp_alert ? "yes" : "no"); conf_write(fp, " initial weight = %d", rs->iweight); conf_write(fp, " effective weight = %" PRIi64, rs->effective_weight); conf_write(fp, " previous effective_weight = %" PRIi64, rs->peffective_weight); conf_write(fp, " alive = %d", rs->alive); conf_write(fp, " num failed checkers = %u", rs->num_failed_checkers); conf_write(fp, " RS set = %d", rs->set); conf_write(fp, " reloaded = %d", rs->reloaded); if (!list_empty(&rs->track_files)) { conf_write(fp, " Tracked Files"); dump_track_file_monitor_list(fp, &rs->track_files); } #ifdef _WITH_BFD_ if (!list_empty(&rs->tracked_bfds)) { conf_write(fp, " Tracked BFDs"); list_for_each_entry(tbfd, &rs->tracked_bfds, e_list) conf_write(fp, " %s", tbfd->bfd->bname); } #endif } static void dump_rs_list(FILE *fp, const list_head_t *l) { real_server_t *rs; list_for_each_entry(rs, l, e_list) dump_rs(fp, rs); } real_server_t * alloc_rs(const char *ip, const char *port) { real_server_t *new; const char *port_str; /* inet_stosockaddr rejects port 0 */ port_str = (port && port[strspn(port, "0")]) ? port : NULL; PMALLOC(new); INIT_LIST_HEAD(&new->e_list); INIT_LIST_HEAD(&new->track_files); #ifdef _WITH_BFD_ INIT_LIST_HEAD(&new->tracked_bfds); #endif INIT_LIST_HEAD(&new->checkers_list); if (inet_stosockaddr(ip, port_str, &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 NULL; } #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 NULL; } #else #if !HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY if (current_vs->af != AF_UNSPEC && new->addr.ss_family != current_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 NULL; } #endif #endif new->effective_weight = INT64_MAX; new->forwarding_method = current_vs->forwarding_method; #ifdef _HAVE_IPVS_TUN_TYPE_ new->tun_type = current_vs->tun_type; new->tun_port = current_vs->tun_port; #ifdef _HAVE_IPVS_TUN_CSUM_ new->tun_flags = current_vs->tun_flags; #endif #endif new->alpha = -1; new->inhibit = -1; new->connection_to = UINT_MAX; new->delay_loop = ULONG_MAX; new->warmup = ULONG_MAX; new->retry = UINT_MAX; new->delay_before_retry = ULONG_MAX; new->virtualhost = NULL; #ifdef _WITH_SNMP_CHECKER_ new->snmp_name = NULL; #endif new->smtp_alert = -1; return new; } /* * Virtual server facility functions */ void free_vs(virtual_server_t *vs) { list_del_init(&vs->e_list); FREE_CONST_PTR(vs->vsgname); FREE_CONST_PTR(vs->virtualhost); #ifdef _WITH_SNMP_CHECKER_ FREE_CONST_PTR(vs->snmp_name); #endif FREE_PTR(vs->s_svr); free_rs_list(&vs->rs); free_notify_script(&vs->notify_quorum_up); free_notify_script(&vs->notify_quorum_down); FREE(vs); } static void free_vs_list(list_head_t *l) { virtual_server_t *vs, *vs_tmp; list_for_each_entry_safe(vs, vs_tmp, l, e_list) free_vs(vs); } static void dump_vs(FILE *fp, const virtual_server_t *vs) { 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); #ifdef _WITH_SNMP_CHECKER_ if (vs->snmp_name) conf_write(fp, " SNMP name = '%s'", vs->snmp_name); #endif if (vs->af != AF_UNSPEC) conf_write(fp, " Address family = inet%s", vs->af == AF_INET ? "" : "6"); else if (vs->vsg && vs->vsg->have_ipv4 && vs->vsg->have_ipv6) conf_write(fp, " Address family = IPv4 & IPv6"); else conf_write(fp, " Address family = unknown"); conf_write(fp, " connection timeout = %s", format_decimal(vs->connection_to, TIMER_HZ_DIGITS)); conf_write(fp, " delay_loop = %s", format_decimal(vs->delay_loop, TIMER_HZ_DIGITS)); conf_write(fp, " lvs_sched = %s", 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 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)" : ""); if (vs->persistence_timeout) conf_write(fp, " persistence timeout = %u", vs->persistence_timeout); if (vs->persistence_granularity != 0xffffffff) { if (vs->af == AF_INET6) conf_write(fp, " persistence granularity = %" PRIu32, 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 == IPPROTO_IP) conf_write(fp, " protocol = none"); else conf_write(fp, " protocol = %d", vs->service_type); conf_write(fp, " alpha is %s", vs->alpha ? "ON" : "OFF"); conf_write(fp, " omega is %s", 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 = %s" , format_decimal(vs->delay_before_retry, TIMER_HZ_DIGITS)); if (vs->warmup != ULONG_MAX) conf_write(fp, " Warmup = %s", format_decimal(vs->warmup, TIMER_HZ_DIGITS)); 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) dump_notify_vs_rs_script(fp, vs->notify_quorum_up, "Quorum", "up"); if (vs->notify_quorum_down) dump_notify_vs_rs_script(fp, vs->notify_quorum_down, "Quorum", "down"); if (vs->ha_suspend) conf_write(fp, " Using HA suspend"); conf_write(fp, " Using smtp notification = %s", vs->smtp_alert ? "yes" : "no"); real_server_t rs = { .forwarding_method = vs->forwarding_method }; #ifdef _HAVE_IPVS_TUN_TYPE_ rs.tun_type = vs->tun_type; rs.tun_port = vs->tun_port; #ifdef _HAVE_IPVS_TUN_CSUM_ rs.tun_flags = vs->tun_flags; #endif #endif dump_forwarding_method(fp, "default ", &rs); if (vs->s_svr) { conf_write(fp, " sorry server %s= %s" , vs->s_svr_duplicates_rs ? "(duplicates rs) " : "" , FMT_RS(vs->s_svr, vs)); dump_forwarding_method(fp, " ", vs->s_svr); conf_write(fp, " Inhibit on failure is %s", vs->s_svr->inhibit ? "ON" : "OFF"); conf_write(fp, " set = %d", vs->s_svr->set); conf_write(fp, " alive = %d", vs->s_svr->alive); } conf_write(fp, " VS alive = %d", vs->alive); conf_write(fp, " quorum_state_up = %d", vs->quorum_state_up); conf_write(fp, " reloaded = %d", vs->reloaded); dump_rs_list(fp, &vs->rs); } static void dump_vs_list(FILE *fp, const list_head_t *l) { virtual_server_t *vs; list_for_each_entry(vs, l, e_list) dump_vs(fp, vs); } virtual_server_t * alloc_vs(const char *param1, const char *param2) { virtual_server_t *new; const char *port_str; unsigned fwmark; PMALLOC(new); INIT_LIST_HEAD(&new->e_list); new->af = AF_UNSPEC; if (!strcmp(param1, "group")) new->vsgname = STRDUP(param2); else if (!strcmp(param1, "fwmark")) { if (!read_unsigned(param2, &fwmark, 0, IPVS_FWMARK_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "virtual server fwmark '%s' must be in [0, %u] - ignoring", param2, IPVS_FWMARK_MAX); skip_block(true); FREE(new); return NULL; } 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 NULL; } 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 NULL; } #endif } new->virtualhost = NULL; #ifdef _WITH_SNMP_CHECKER_ new->snmp_name = NULL; #endif 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->connection_to = 5 * TIMER_HZ; 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; new->persistence_granularity = 0xffffffff; INIT_LIST_HEAD(&new->rs); return new; } /* Sorry server facility functions */ void alloc_ssvr(const char *ip, const char *port) { real_server_t *new; const char *port_str; /* inet_stosockaddr rejects port 0 */ port_str = (port && port[strspn(port, "0")]) ? port : NULL; PMALLOC(new); new->effective_weight = 1; new->iweight = 1; new->forwarding_method = current_vs->forwarding_method; #ifdef _HAVE_IPVS_TUN_TYPE_ new->tun_type = current_vs->tun_type; new->tun_port = current_vs->tun_port; #ifdef _HAVE_IPVS_TUN_CSUM_ new->tun_flags = current_vs->tun_flags; #endif #endif if (inet_stosockaddr(ip, port_str, &new->addr)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid sorry server IP address %s - skipping", ip); FREE(new); return; } current_vs->s_svr = new; } #ifdef _WITH_BFD_ /* Track bfd dump */ static void dump_checker_bfd(FILE *fp, const checker_tracked_bfd_t *cbfd) { conf_write(fp, " Checker Track BFD = %s", cbfd->bname); // conf_write(fp, " Weight = %d", cbfd->weight); conf_write(fp, " Tracking RS :"); dump_bfds_rs_list(fp, &cbfd->tracking_rs); } static void dump_checker_bfd_list(FILE *fp, const list_head_t *l) { checker_tracked_bfd_t *cbfd; list_for_each_entry(cbfd, l, e_list) dump_checker_bfd(fp, cbfd); } void free_checker_bfd(checker_tracked_bfd_t *cbfd) { list_del_init(&cbfd->e_list); FREE(cbfd->bname); free_bfds_rs_list(&cbfd->tracking_rs); FREE(cbfd); } static void free_checker_bfd_list(list_head_t *l) { checker_tracked_bfd_t *cbfd, *cbfd_tmp; list_for_each_entry_safe(cbfd, cbfd_tmp, l, e_list) free_checker_bfd(cbfd); } #endif /* data facility functions */ check_data_t * alloc_check_data(void) { check_data_t *new; PMALLOC(new); INIT_LIST_HEAD(&new->vs); INIT_LIST_HEAD(&new->vs_group); INIT_LIST_HEAD(&new->track_files); #ifdef _WITH_BFD_ INIT_LIST_HEAD(&new->track_bfds); #endif return new; } void free_check_data(check_data_t **datap) { check_data_t *data = *datap; free_vs_list(&data->vs); free_vsg_list(&data->vs_group); free_track_file_list(&data->track_files); #ifdef _WITH_BFD_ free_checker_bfd_list(&data->track_bfds); #endif FREE(data); *datap = NULL; } static void dump_check_data(FILE *fp, const check_data_t *data) { if (data->ssl) { conf_write(fp, "------< SSL definitions >------"); dump_ssl(fp); } if (!list_empty(&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_empty(&data->vs_group)) dump_vsg_list(fp, &data->vs_group); dump_vs_list(fp, &data->vs); } dump_checkers(fp); if (!list_empty(&data->track_files)) { conf_write(fp, "------< Checker track files >------"); dump_track_file_list(fp, &data->track_files); } #ifdef _WITH_BFD_ if (!list_empty(&data->track_bfds)) { conf_write(fp, "------< Checker track BFDs >------"); dump_checker_bfd_list(fp, &data->track_bfds); } #endif } void dump_data_check(FILE *fp) { dump_global_data(fp, global_data); dump_check_data(fp, check_data); } const char * format_vs(const 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; } const char * format_vsge(const virtual_server_group_entry_t *vsge) { static char ret[INET6_ADDRSTRLEN + 1 + INET6_ADDRSTRLEN + 1 + 5 + 1]; /* IPv6 addr-IPv6 addr:ppppp */ unsigned offs; if (vsge->is_fwmark) snprintf(ret, sizeof(ret), "FWM %u", vsge->vfwmark); else if (inet_sockaddrcmp(&vsge->addr, &vsge->addr_end)) { offs = snprintf(ret, sizeof(ret), "%s-", inet_sockaddrtos(&vsge->addr)); snprintf(ret + offs, sizeof(ret) - offs, "%s,%d", inet_sockaddrtos(&vsge->addr_end), ntohs(inet_sockaddrport(&vsge->addr))); } else snprintf(ret, sizeof(ret), "%s,%d", inet_sockaddrtos(&vsge->addr), ntohs(inet_sockaddrport(&vsge->addr))); return ret; } const char * format_rs(const real_server_t *rs, const virtual_server_t *vs) { static char buf[SOCKADDRTRIO_STR_LEN]; inet_sockaddrtotrio_r(&rs->addr, vs->service_type, buf); return buf; } static void check_check_script_security(void) { virtual_server_t *vs; real_server_t *rs; unsigned script_flags; magic_t magic; if (list_empty(&check_data->vs)) return; magic = ka_magic_open(); script_flags = check_misc_script_security(magic); list_for_each_entry(vs, &check_data->vs, e_list) { script_flags |= check_notify_script_secure(&vs->notify_quorum_up, magic); script_flags |= check_notify_script_secure(&vs->notify_quorum_down, magic); list_for_each_entry(rs, &vs->rs, e_list) { 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) { virtual_server_t *vs, *vs_tmp; virtual_server_group_entry_t *vsge; real_server_t *rs, *rs_tmp, *rs1; checker_t *checker; unsigned weight_sum; bool rs_removed; using_ha_suspend = false; list_for_each_entry_safe(vs, vs_tmp, &check_data->vs, e_list) { /* Ensure that ha_suspend is not set for any virtual server using fwmarks */ if (vs->ha_suspend && (vs->vfwmark || (vs->vsg && !list_empty(&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_empty(&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 */ report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s: no protocol set - defaulting to UDP", FMT_VS(vs)); vs->service_type = IPPROTO_UDP; } /* 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; } /* Check port specified for udp/tcp/sctp unless persistent */ if (!vs->persistence_timeout && !vs->vsg && !vs->vfwmark && !inet_sockaddrport(&vs->addr)) { 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_for_each_entry(vsge, &vs->vsg->addr_range, e_list) { if (!inet_sockaddrport(&vsge->addr)) { 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_empty(&vs->vsg->addr_range) && !list_empty(&vs->vsg->vfwmark)) || (!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)); vs->service_type = 0; } /* Check scheduler set */ if (!vs->sched[0]) { report_config_error(CONFIG_GENERAL_ERROR, "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 */ weight_sum = 0; list_for_each_entry_safe(rs, rs_tmp, &vs->rs, e_list) { /* Check the real server is not a duplicate of any rs earlier in the list */ rs_removed = false; list_for_each_entry(rs1, &vs->rs, e_list) { if (rs == rs1) break; if (rs_iseq(rs, rs1)) { report_config_error(CONFIG_GENERAL_ERROR, "VS %s: real server %s is duplicated - removing second rs", FMT_VS(vs), FMT_RS(rs, vs)); free_rs(rs); #ifdef _WITH_SNMP_CHECKER_ vs->rs_cnt--; #endif rs_removed = true; break; } } if (rs_removed) continue; /* 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) { report_config_error(CONFIG_GENERAL_ERROR, "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; #ifdef _HAVE_IPVS_TUN_TYPE_ rs->tun_type = vs->tun_type; rs->tun_port = vs->tun_port; #ifdef _HAVE_IPVS_TUN_CSUM_ rs->tun_flags = vs->tun_flags; #endif #endif } /* 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->connection_to == UINT_MAX) rs->connection_to = vs->connection_to; 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->effective_weight == INT64_MAX) { rs->effective_weight = vs->weight; rs->iweight = rs->effective_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; } } weight_sum += rs->effective_weight; /* Check if the real server is the same as the sorry server, * and if so the inhibit on failure settings must match. */ if (vs->s_svr && rs_iseq(vs->s_svr, rs)) { if (vs->s_svr->inhibit != rs->inhibit) { report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s: real server %s matches sorry server, but inhibit setting differs, %sing on sorry server", FMT_VS(vs), FMT_RS(rs, vs), rs->inhibit ? "sett" : "clear"); vs->s_svr->inhibit = rs->inhibit; } vs->s_svr_duplicates_rs = true; } } if (vs->s_svr && vs->s_svr->forwarding_method == IP_VS_CONN_F_FWD_MASK) { if (vs->forwarding_method == IP_VS_CONN_F_FWD_MASK) { report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s: no forwarding method set, setting default NAT", FMT_VS(vs)); vs->forwarding_method = IP_VS_CONN_F_MASQ; } vs->s_svr->forwarding_method = vs->forwarding_method; #ifdef _HAVE_IPVS_TUN_TYPE_ vs->s_svr->tun_type = vs->tun_type; vs->s_svr->tun_port = vs->tun_port; #ifdef _HAVE_IPVS_TUN_CSUM_ vs->s_svr->tun_flags = vs->tun_flags; #endif #endif } /* Check that the quorum isn't higher than the total weight of * the real servers, otherwise we will never be able to come up. */ // TODO - Allow 253 * multiplier per MISC_CHECK if !reverse and ignore this if FILE_CHECK if (vs->quorum > weight_sum) { report_config_error(CONFIG_GENERAL_ERROR, "Warning - quorum %u for %s exceeds total weight of real servers %u, reducing quorum to %u", vs->quorum, FMT_VS(vs), weight_sum, weight_sum); vs->quorum = weight_sum; } /* 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; } /* Now check that, unless using NAT, real and virtual servers have the same port. * Also if a fwmark is used, ensure that unless NAT, the real server port is 0. */ if (vs->vsg) { if (!list_empty(&vs->vsg->vfwmark)) { list_for_each_entry(rs, &vs->rs, e_list) { if (rs->forwarding_method == IP_VS_CONN_F_MASQ) continue; if (inet_sockaddrport(&rs->addr)) report_config_error(CONFIG_GENERAL_ERROR, "WARNING - fwmark virtual server %s, real server %s has port specified - port will be ignored", FMT_VS(vs), FMT_RS(rs, vs)); } if (vs->s_svr && vs->s_svr->forwarding_method != IP_VS_CONN_F_MASQ && inet_sockaddrport(&vs->s_svr->addr)) report_config_error(CONFIG_GENERAL_ERROR, "WARNING - fwmark virtual server %s, sorry server has port specified - port will be ignored", FMT_VS(vs)); } list_for_each_entry(vsge, &vs->vsg->addr_range, e_list) { list_for_each_entry(rs, &vs->rs, e_list) { if (rs->forwarding_method == IP_VS_CONN_F_MASQ) continue; if (inet_sockaddrport(&rs->addr) && inet_sockaddrport(&vsge->addr) != inet_sockaddrport(&rs->addr)) report_config_error(CONFIG_GENERAL_ERROR, "virtual server %s:[%s] and real server %s ports don't match", FMT_VS(vs), format_vsge(vsge), FMT_RS(rs, vs)); } if (vs->s_svr && vs->s_svr->forwarding_method != IP_VS_CONN_F_MASQ && inet_sockaddrport(&vs->s_svr->addr) && inet_sockaddrport(&vsge->addr) != inet_sockaddrport(&vs->s_svr->addr)) report_config_error(CONFIG_GENERAL_ERROR, "WARNING - virtual server %s, sorry server has port specified - port will be ignored", FMT_VS(vs)); } } else { /* We can also correct errors here */ list_for_each_entry(rs, &vs->rs, e_list) { if (rs->forwarding_method == IP_VS_CONN_F_MASQ) { if (!vs->vfwmark && !inet_sockaddrport(&rs->addr)) inet_set_sockaddrport(&rs->addr, inet_sockaddrport(&vs->addr)); continue; } if (vs->vfwmark) { if (inet_sockaddrport(&rs->addr)) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - fwmark virtual server %s, real server %s has port specified - clearing", FMT_VS(vs), FMT_RS(rs, vs)); inet_set_sockaddrport(&rs->addr, 0); } } else { if (!inet_sockaddrport(&rs->addr)) inet_set_sockaddrport(&rs->addr, inet_sockaddrport(&vs->addr)); else if (inet_sockaddrport(&vs->addr) != inet_sockaddrport(&rs->addr)) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - virtual server %s and real server %s ports don't match - resetting", FMT_VS(vs), FMT_RS(rs, vs)); inet_set_sockaddrport(&rs->addr, inet_sockaddrport(&vs->addr)); } } } /* Check any sorry server */ if (vs->s_svr && vs->s_svr->forwarding_method != IP_VS_CONN_F_MASQ) { if (vs->vfwmark) { if (inet_sockaddrport(&vs->s_svr->addr)) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - virtual server %s, sorry server has port specified - clearing", FMT_VS(vs)); inet_set_sockaddrport(&vs->s_svr->addr, 0); } } else { if (!inet_sockaddrport(&vs->s_svr->addr)) inet_set_sockaddrport(&vs->s_svr->addr, inet_sockaddrport(&vs->addr)); else if (inet_sockaddrport(&vs->addr) != inet_sockaddrport(&vs->s_svr->addr)) { report_config_error(CONFIG_GENERAL_ERROR, "WARNING - virtual server %s and sorry server ports don't match - resetting", FMT_VS(vs)); inet_set_sockaddrport(&vs->s_svr->addr, inet_sockaddrport(&vs->addr)); } } } } } list_for_each_entry(vs, &check_data->vs, e_list) { list_for_each_entry(rs, &vs->rs, e_list) { list_for_each_entry(checker, &rs->checkers_list, rs_list) { /* 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->co && checker->co->connection_to == UINT_MAX) checker->co->connection_to = checker->rs->connection_to; 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 any checker that hasn't run as failed. * Reloading is handled in migrate_checkers() */ if (!reload) { if (checker->alpha) { set_checker_state(checker, false); UNSET_ALIVE(checker->rs); } /* For non alpha mode, one failure is enough initially. * For alpha mode, log failure after one failure */ 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; } keepalived-2.3.3/keepalived/check/check_tcp.c0000664000175000017500000001457214745724200014606 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: TCP checker. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include "check_tcp.h" #include "check_api.h" #include "memory.h" #include "ipwrapper.h" #include "layer4.h" #include "logger.h" #include "smtp.h" #include "utils.h" #include "parser.h" #ifdef THREAD_DUMP #include "scheduler.h" #endif #include "check_parser.h" static void tcp_connect_thread(thread_ref_t); /* Configuration stream handling */ static void free_tcp_check(checker_t *checker) { FREE(checker->co); FREE(checker); } static void dump_tcp_check(FILE *fp, __attribute__((unused)) const checker_t *checker) { conf_write(fp, " Keepalive method = TCP_CHECK"); } static bool compare_tcp_check(const checker_t *old_c, checker_t *new_c) { return compare_conn_opts(old_c->co, new_c->co); } static const checker_funcs_t tcp_checker_funcs = { CHECKER_TCP, free_tcp_check, dump_tcp_check, compare_tcp_check, NULL }; static void tcp_check_handler(__attribute__((unused)) const vector_t *strvec) { /* queue new checker */ queue_checker(&tcp_checker_funcs, tcp_connect_thread, NULL, CHECKER_NEW_CO(), true); } static void tcp_check_end_handler(void) { if (!check_conn_opts(current_checker->co)) { dequeue_new_checker(); return; } } void install_tcp_check_keyword(void) { vpp_t check_ptr; install_keyword("TCP_CHECK", &tcp_check_handler); check_ptr = install_sublevel(VPP ¤t_checker); install_checker_common_keywords(true); install_level_end_handler(tcp_check_end_handler); install_sublevel_end(check_ptr); } static void tcp_epilog(thread_ref_t thread, bool is_success) { checker_t *checker; unsigned long delay; bool checker_was_up; bool rs_was_alive; checker = THREAD_ARG(thread); delay = checker->delay_loop; if (is_success || ((checker->is_up || !checker->has_run) && checker->retry_it >= checker->retry)) { checker->retry_it = 0; if (is_success && (!checker->is_up || !checker->has_run)) { log_message(LOG_INFO, "TCP connection to %s success." , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(UP, checker); if (checker->rs->smtp_alert && !checker_was_up && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> TCP CHECK succeed on service <="); } else if (!is_success && (checker->is_up || !checker->has_run)) { if (checker->retry && checker->has_run) log_message(LOG_INFO , "TCP_CHECK on service %s failed after %u retries." , FMT_CHK(checker) , checker->retry); else log_message(LOG_INFO , "TCP_CHECK on service %s failed." , FMT_CHK(checker)); checker_was_up = checker->is_up; rs_was_alive = checker->rs->alive; update_svr_checker_state(DOWN, checker); if (checker->rs->smtp_alert && checker_was_up && (rs_was_alive != checker->rs->alive || !global_data->no_checker_emails)) smtp_alert(SMTP_MSG_RS, checker, NULL, "=> TCP CHECK failed on service <="); } } else if (checker->is_up) { delay = checker->delay_before_retry; ++checker->retry_it; } checker->has_run = true; /* Register next timer checker */ thread_add_timer(thread->master, tcp_connect_thread, checker, delay); } static void tcp_check_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); int status; status = tcp_socket_state(thread, tcp_check_thread, 0); /* If status = connect_in_progress, next thread is already registered. * If it is connect_success, the fd is still open. * Otherwise we have a real connection error or connection timeout. */ switch(status) { case connect_in_progress: break; case connect_success: thread_close_fd(thread); tcp_epilog(thread, true); break; case connect_timeout: if (checker->is_up && (global_data->checker_log_all_failures || checker->log_all_failures)) log_message(LOG_INFO, "TCP connection to %s timeout." , FMT_CHK(checker)); tcp_epilog(thread, false); break; default: if (checker->is_up && (global_data->checker_log_all_failures || checker->log_all_failures)) log_message(LOG_INFO, "TCP connection to %s failed." , FMT_CHK(checker)); tcp_epilog(thread, false); } } static void tcp_connect_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); conn_opts_t *co = checker->co; int fd; int status; /* * Register a new checker thread & return * if checker is disabled */ if (!checker->enabled) { thread_add_timer(thread->master, tcp_connect_thread, checker, checker->delay_loop); return; } if ((fd = socket(co->dst.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_TCP)) == -1) { log_message(LOG_INFO, "TCP connect fail to create socket. Rescheduling."); thread_add_timer(thread->master, tcp_connect_thread, checker, checker->delay_loop); return; } status = tcp_bind_connect(fd, co); /* handle tcp connection status & register check worker thread */ if(tcp_connection_state(fd, status, thread, tcp_check_thread, co->connection_to, 0)) { close(fd); if (status == connect_fail) { tcp_epilog(thread, false); } else { log_message(LOG_INFO, "TCP socket bind failed. Rescheduling."); thread_add_timer(thread->master, tcp_connect_thread, checker, checker->delay_loop); } } } #ifdef THREAD_DUMP void register_check_tcp_addresses(void) { register_thread_address("tcp_check_thread", tcp_check_thread); register_thread_address("tcp_connect_thread", tcp_connect_thread); } #endif keepalived-2.3.3/keepalived/check/check_ssl.c0000664000175000017500000002546114661620303014614 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: SSL GET CHECK. Perform an ssl get query to a specified * url, compute a MD5 over this result and match it to the * expected value. * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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, */ #include "config.h" #include #include #include #include "check_ssl.h" #include "check_api.h" #include "check_http.h" #include "logger.h" #ifdef THREAD_DUMP #include "scheduler.h" #endif /* SSL primitives */ /* Free an SSL context */ void clear_ssl(ssl_data_t *ssl) { if (ssl && ssl->ctx) { SSL_CTX_free(ssl->ctx); ssl->ctx = NULL; } } /* PEM password callback function */ static int password_cb(char *buf, int num, __attribute__((unused)) int rwflag, void *userdata) { ssl_data_t *ssl = PTR_CAST(ssl_data_t, userdata); size_t plen = strlen(ssl->password); if ((unsigned)num < plen + 1) return (0); strcpy(buf, ssl->password); return (int)plen; } /* Inititalize global SSL context */ static bool build_ssl_ctx(void) { ssl_data_t *ssl; /* Library initialization */ #ifdef HAVE_OPENSSL_INIT_CRYPTO #ifndef HAVE_OPENSSL_INIT_NO_LOAD_CONFIG_BUG /* In OpenSSL v1.1.1 if the following is called, SSL_CTX_new() below fails. * It works in v1.1.0h and v1.1.1b. * It transpires that it works without setting NO_LOAD_CONFIG, but it is * presumably more efficient not to load it. */ if (!OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG, NULL)) log_message(LOG_INFO, "OPENSSL_init_crypto failed"); #endif #else SSL_library_init(); SSL_load_error_strings(); #endif if (!check_data->ssl) { PMALLOC(ssl); } else ssl = check_data->ssl; /* Initialize SSL context */ #ifdef HAVE_TLS_METHOD ssl->meth = TLS_method(); #else ssl->meth = SSLv23_method(); #endif if (!(ssl->ctx = SSL_CTX_new(ssl->meth))) { log_message(LOG_INFO, "SSL error: cannot create new SSL context"); if (!check_data->ssl) FREE(ssl); return false; } /* return for autogen context */ if (!check_data->ssl) { check_data->ssl = ssl; goto end; } /* Load our keys and certificates */ if (check_data->ssl->certfile) if (!(SSL_CTX_use_certificate_chain_file(ssl->ctx, check_data->ssl->certfile))) { log_message(LOG_INFO, "SSL error : Cant load certificate file..."); return false; } /* Handle password callback using userdata ssl */ if (check_data->ssl->password) { SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, check_data->ssl); SSL_CTX_set_default_passwd_cb(ssl->ctx, password_cb); } if (check_data->ssl->keyfile) if (!(SSL_CTX_use_PrivateKey_file(ssl->ctx, check_data->ssl->keyfile, SSL_FILETYPE_PEM))) { log_message(LOG_INFO, "SSL error : Cant load key file..."); return false; } /* Load the CAs we trust */ if (check_data->ssl->cafile) if (!(SSL_CTX_load_verify_locations(ssl->ctx, check_data->ssl->cafile, 0))) { log_message(LOG_INFO, "SSL error : Cant load CA file..."); return false; } end: #if HAVE_SSL_CTX_SET_VERIFY_DEPTH SSL_CTX_set_verify_depth(ssl->ctx, 1); #endif return true; } /* * Initialize the SSL context, with or without specific * configuration files. */ bool init_ssl_ctx(void) { ssl_data_t *ssl = check_data->ssl; if (!build_ssl_ctx()) { log_message(LOG_INFO, "Error Initialize SSL, ctx Instance"); log_message(LOG_INFO, " SSL keyfile:%s", ssl->keyfile); log_message(LOG_INFO, " SSL password:%s", ssl->password); log_message(LOG_INFO, " SSL cafile:%s", ssl->cafile); log_message(LOG_INFO, "Terminate..."); clear_ssl(ssl); return false; } return true; } /* Display SSL error to readable string */ int ssl_printerr(int err) { switch (err) { case SSL_ERROR_ZERO_RETURN: log_message(LOG_INFO, " SSL error: (zero return)"); break; case SSL_ERROR_WANT_READ: log_message(LOG_INFO, " SSL error: (read error)"); break; case SSL_ERROR_WANT_WRITE: log_message(LOG_INFO, " SSL error: (write error)"); break; case SSL_ERROR_WANT_CONNECT: log_message(LOG_INFO, " SSL error: (connect error)"); break; case SSL_ERROR_WANT_X509_LOOKUP: log_message(LOG_INFO, " SSL error: (X509 lookup error)"); break; case SSL_ERROR_SYSCALL: log_message(LOG_INFO, " SSL error: (syscall error)"); break; case SSL_ERROR_SSL: /* Note: the following is not thread safe. Use MALLOC(256) and ERR_error_string_n if need thread safety */ log_message(LOG_INFO, " SSL error: (%s)", ERR_error_string(ERR_get_error(), NULL)); break; } return 0; } int ssl_connect(thread_ref_t thread, int new_req) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); request_t *req = http_get_check->req; #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ url_t *url = http_get_check->url_it; /* The man page for SSL_set_tlsext_host_name states name is const char *, * but it is cast to a void * */ union { const char *name_const; char *name; } vhost; #endif int ret; /* First round, create SSL context */ if (new_req) { int bio_fd; if (!(req->ssl = SSL_new(check_data->ssl->ctx))) { log_message(LOG_INFO, "Unable to establish ssl connection - SSL_new() failed"); return 0; } if (!(req->bio = BIO_new_socket(thread->u.f.fd, BIO_NOCLOSE))) { log_message(LOG_INFO, "Unable to establish ssl connection - BIO_new_socket() failed"); return 0; } BIO_get_fd(req->bio, &bio_fd); if (bio_fd != thread->u.f.fd) { if (fcntl(bio_fd, F_SETFD, fcntl(bio_fd, F_GETFD) | FD_CLOEXEC) == -1) log_message(LOG_INFO, "Setting CLOEXEC failed on ssl socket - errno %d", errno); } /* There is a memory leak in openSSL at least in version 3.0.1, which is fixed * by version 3.0.5. It was not present in version 1.1.1n. Since I haven't been * able to identify the OpenSSL patch that resolved the leak, we play safe and * assume it is in versions 3.0.0 up to 3.0.4. * The leak is memory allocated by * p = OPENSSL_malloc(len); * in ssl3_setup_write_buffer() in ssl/record/ssl_buffer.c * * It appears that setting SSL_MODE_RELEASE_BUFFERS causes the memory leak not * to occur. */ #ifdef OPENSSL_VERSION_MAJOR #if OPENSSL_VERSION_MAJOR == 3 && OPENSSL_VERSION_MINOR == 0 && OPENSSL_VERSION_PATCH <= 4 SSL_set_mode(req->ssl, SSL_MODE_RELEASE_BUFFERS); #endif #endif if (!url->tls_compliant && !http_get_check->tls_compliant) SSL_set_quiet_shutdown(req->ssl, 1); #if defined HAVE_SSL_SET0_RBIO && defined HAVE_SSL_SET0_WBIO BIO_up_ref(req->bio); SSL_set0_rbio(req->ssl, req->bio); SSL_set0_wbio(req->ssl, req->bio); #else SSL_set_bio(req->ssl, req->bio, req->bio); #endif #ifdef _HAVE_SSL_SET_TLSEXT_HOST_NAME_ if (http_get_check->enable_sni) { if (url && url->virtualhost) vhost.name_const = url->virtualhost; else if (http_get_check->virtualhost) vhost.name_const = http_get_check->virtualhost; else if (checker->vs->virtualhost) vhost.name_const = checker->vs->virtualhost; else vhost.name_const = NULL; if (vhost.name) SSL_set_tlsext_host_name(req->ssl, vhost.name); } #endif } ret = SSL_connect(req->ssl); return ret; } bool ssl_send_request(SSL * ssl, const char *str_request, int request_len) { int r; r = SSL_write(ssl, str_request, request_len); return (r == request_len); } /* Asynchronous SSL stream reader */ void ssl_read_thread(thread_ref_t thread) { checker_t *checker = THREAD_ARG(thread); http_checker_t *http_get_check = CHECKER_ARG(checker); request_t *req = http_get_check->req; url_t *url = http_get_check->url_it; unsigned long timeout; unsigned char digest[MD5_DIGEST_LENGTH]; int r = 0; /* Handle read timeout */ if (thread->type == THREAD_READ_TIMEOUT) { SSL_shutdown(req->ssl); timeout_epilog(thread, "Timeout SSL read"); return; } timeout = timer_long(thread->sands) - timer_long(time_now); /* read the SSL stream - allow for terminating the data with '\0 */ r = SSL_read(req->ssl, req->buffer + req->len, (int)(MAX_BUFFER_LENGTH - 1 - req->len)); if (r > 0) { /* Handle response stream */ http_process_response(thread, req, (size_t)r, url); /* * Register next ssl stream reader. * Register itself to not perturbe global I/O multiplexer. */ thread_add_read(thread->master, ssl_read_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } req->error = SSL_get_error(req->ssl, r); if (req->error == SSL_ERROR_WANT_READ) { /* async read unfinished */ thread_add_read(thread->master, ssl_read_thread, checker, thread->u.f.fd, timeout, THREAD_DESTROY_CLOSE_FD); return; } if (req->error == SSL_ERROR_SSL) { const char *file; int line; #ifdef HAVE_ERR_GET_ERROR_ALL const char *func; #endif const char *data; int flags; unsigned long err; #ifdef HAVE_ERR_GET_ERROR_ALL while ((err = ERR_get_error_all(&file, &line, &func, &data, &flags))) { #else while ((err = ERR_get_error_line_data(&file, &line, &data, &flags))) { #endif /* Don't output the same error message repeatedly */ if (err == url->last_ssl_error) continue; url->last_ssl_error = err; log_message(LOG_INFO, "SSL error '%s' at %s:%d " #ifdef HAVE_ERR_GET_ERROR_ALL "(%s) " #endif "data '%s' flags 0x%x", ERR_error_string(err, NULL), file, line, #ifdef HAVE_ERR_GET_ERROR_ALL func, #endif data, (unsigned)flags); } } /* All the SSL stream has been parsed */ if (url->digest) { EVP_DigestFinal_ex(req->context, digest, NULL); EVP_MD_CTX_free(req->context); req->context = NULL; if (req->error == SSL_ERROR_ZERO_RETURN && http_get_check->genhash_flags & GENHASH_VERBOSE) dump_digest(digest, MD5_DIGEST_LENGTH); } else digest[0] = 0; if (req->error != SSL_ERROR_SSL && req->error != SSL_ERROR_SYSCALL) r = SSL_shutdown(req->ssl); else r = 0; if (r && !req->extracted) { timeout_epilog(thread, "SSL read error from"); return; } /* Handle response stream */ http_handle_response(thread, digest, !req->extracted); } #ifdef THREAD_DUMP void register_check_ssl_addresses(void) { register_thread_address("ssl_read_thread", ssl_read_thread); } #endif keepalived-2.3.3/keepalived/keepalived.config-opts.in0000664000175000017500000000002114555524620016315 @CONFIG_OPTIONS@ keepalived-2.3.3/keepalived/main.c0000664000175000017500000000212112752660317012521 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Main program stub. * * Author: Quentin Armitage, * * 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) 2016-2016 Alexandre Cassen, */ #include "config.h" #include "main.h" int main(int argc, char **argv) { return keepalived_main(argc, argv); } keepalived-2.3.3/keepalived/keepalived-non-root.service.in0000664000175000017500000001111114457775050017306 # This systemd service file allows keepalived to be run as a non-root user. # To use this, edit the permissions according to your needs, and install the # file in /usr/lib/systemd/system as keepalived.service # # If your version of systemd is v228 or older, edit this file to change # "AmbientCapabilities=" to "Capabilities=". If that still doesn't work, # use setcap(8) to set the capabilities listed below on the keepalived # executable file. [Unit] Description=LVS and VRRP High Availability Monitor After=network-online.target syslog.target @SNMP_SERVICE@ Requires=network-online.target Wants=@SNMP_SERVICE@ Documentation=man:keepalived(8) Documentation=man:keepalived.conf(5) Documentation=man:genhash(1) Documentation=https://keepalived.org StartLimitBurst=3 StartLimitIntervalSec=10 [Service] Type=@SYSTEMD_SERVICE_TYPE@ NotifyAccess=main PIDFile=@RUN_DIR@/run/%N/%N.pid # CAP_CHOWN needed if using FIFOs and specify the owner/group AmbientCapabilities=CAP_CHOWN # CAP_KILL needed if running notify scripts, FIFO scripts, or using track_scripts, CHECK_MISC or startup/shutdown scripts AmbientCapabilities=CAP_KILL # CAP_NET_ADMIN is needed for VRRP, IPVS AmbientCapabilities=CAP_NET_ADMIN # CAP_NET_BIND_SERVICE needed for VRRP AmbientCapabilities=CAP_NET_BIND_SERVICE # CAP_NET_RAW needed for VRRP and IPVS if not using netlink (unlikely) AmbientCapabilities=CAP_NET_RAW # CAP_SETUID and CAP_SETGID needed if running any scripts and user keepalived_script exists or the user/group of any script is specified AmbientCapabilities=CAP_SETUID AmbientCapabilities=CAP_SETGID # CAP_NET_MODULE needed to load ip_vs module (IPVS) and xt_set (VRRP with iptables) # Alternatively add a file in /usr/lib/modules-load.d with ip_vs and xt_set and # don't enable CAP_NET_MODULE, and set ProtectKernelModules=yes AmbientCapabilities=CAP_SYS_MODULE # CAP_SYS_NICE needed for keepalived to set its nice priority. If in use, also remove LimitNICE=0 AmbientCapabilities=CAP_SYS_NICE # CAP_SYS_RESOURCE needed for keepalived to adjust its realtime priority, or to increase the number of sockets (files) or corefile size. # If not allowing realtime scheduling, and LimitNOFILE and LimitCORE are specified below AmbientCapabilities=CAP_SYS_RESOURCE CacheDirectory=%N CacheDirectoryMode=0750 # Each capability allowed in AmbientCapabilities needs to have a corresponding CapabilityBoundingSet=CAP_... below CapabilityBoundingSet=CAP_CHOWN CapabilityBoundingSet=CAP_KILL CapabilityBoundingSet=CAP_NET_ADMIN CapabilityBoundingSet=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_RAW CapabilityBoundingSet=CAP_SETUID CapabilityBoundingSet=CAP_SETGID CapabilityBoundingSet=CAP_SYS_MODULE CapabilityBoundingSet=CAP_SYS_NICE CapabilityBoundingSet=CAP_SYS_RESOURCE ConfigurationDirectory=%N ConfigurationDirectoryMode=0755 # Enable the following to make keepalived run with realtime scheduling # CPUScheduling=rr DeviceAllow=/dev/null DevicePolicy=strict DynamicUser=yes # Edit the following line if needed - for Ubuntu it should be -@sysconfdir/default/%N EnvironmentFile=-@sysconfdir@/sysconfig/%N ExecReload=/bin/kill -HUP $MAINPID ExecStart=@sbindir@/keepalived --dont-fork $DAEMON_ARGS $KEEPALIVED_OPTIONS --pid=@RUN_DIR@/run/%N/%N.pid --vrrp_pid=@RUN_DIR@/run/%N/vrrp.pid --checkers_pid=@RUN_DIR@/run/%N/%N_checkers.pid --bfd_pid=@RUN_DIR@/run/%N/%N_bfd.pid IOSchedulingClass=realtime KillMode=process KillSignal=SIGTERM # Set the following if not allowing CAP_SYS_RESOURCE # LimitCORE=infinity LimitMEMLOCK=infinity # Add LimitNICE=0 if using realtime scheduling or to stop keepalived increasing its priority # LimitNICE=0 # Set the following if not allowing CAP_SYS_RESOURCE # LimitNOFILE=500000 LockPersonality=yes LogsDirectory=%N LogsDirectoryMode=0750 NoNewPrivileges=yes OOMScoreAdjust=-500 PrivateTmp=yes ProtectControlGroups=yes ProtectHome=read-only # ProtectKernelModules=no needed to load ip_vs module (IPVS) and xt_set (VRRP with iptables), otherwise set to yes ProtectKernelModules=no # ProtectKernelTunables=no keeded for VRRP using VMACs ProtectKernelTunables=no ProtectSystem=strict RemoveIPC=yes Restart=always RestartSec=5 # AF_INET if using any IPv4, AF_INET6 if using any IPv6. AF_NETLINK for VRRP and IPVS. AF_PACKET for VRRP. AF_UNIX always needed. RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK AF_UNIX AF_PACKET RestrictNamespaces=yes # Set to yes to disable keepalived turning on realtime scheduling and increasing priority RestrictRealtime=no RuntimeDirectory=%N RuntimeDirectoryMode=0750 SystemCallArchitectures=native UMask=0007 User=_%N Group=%N StateDirectory=%N StateDirectoryMode=0750 TimeoutStopSec=30 [Install] WantedBy=multi-user.target keepalived-2.3.3/keepalived/Makefile.in0000664000175000017500000007650014772274255013520 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : sbin_PROGRAMS = keepalived$(EXEEXT) @INIT_SYSTEMD_TRUE@am__append_1 = keepalived.service keeplived-non-root.service @REPRODUCIBLE_BUILD_TRUE@am__append_2 = keepalived.config-opts subdir = keepalived ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(configdir)" \ "$(DESTDIR)$(initdir)" "$(DESTDIR)$(systemdsystemunitdir)" PROGRAMS = $(sbin_PROGRAMS) am_keepalived_OBJECTS = main.$(OBJEXT) keepalived_OBJECTS = $(am_keepalived_OBJECTS) am__DEPENDENCIES_1 = keepalived_DEPENDENCIES = core/libcore.a $(IPVS_LIB) $(VRRP_LIB) \ $(BFD_LIB) core/libcore.a $(TRACKER_LIB) ../lib/liblib.a \ $(am__DEPENDENCIES_1) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/lib depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/main.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(keepalived_SOURCES) DIST_SOURCES = $(keepalived_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } DATA = $(config_DATA) $(init_DATA) $(noinst_DATA) \ $(systemdsystemunit_DATA) HEADERS = $(noinst_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` DIST_SUBDIRS = core vrrp check dbus bfd trackers etc am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib \ $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) edit = echo " EDIT $@"; \ @SED@ \ -e 's|@sbindir[@]|$(sbindir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ -e 's|@RUN_DIR[@]|$(RUN_DIR)|g' \ -e 's|@SNMP_SERVICE[@]|$(SNMP_SERVICE)|g' \ -e 's|@SYSTEMD_SERVICE_TYPE[@]|$(SYSTEMD_SERVICE_TYPE)|g' \ -e 's|@SYSTEMD_EXEC_START_OPTIONS[@]|$(SYSTEMD_EXEC_START_OPTIONS)|g' \ -e '/^Wants= *$$/d' \ -e 's|@CONFIG_OPTIONS[@]|$(KEEPALIVED_CONFIG_OPTIONS)|g' keepalived_SOURCES = main.c noinst_HEADERS = $(srcdir)/include/*.h TRACKER_SUBDIR = trackers TRACKER_LIB = trackers/libtracker.a @WITH_IPVS_TRUE@IPVS_SUBDIR = check @WITH_IPVS_TRUE@IPVS_LIB = check/libcheck.a @WITH_VRRP_TRUE@VRRP_SUBDIR = vrrp @WITH_VRRP_TRUE@VRRP_LIB = vrrp/libvrrp.a @WITH_DBUS_TRUE@DBUS_SUBDIR = dbus @WITH_BFD_TRUE@BFD_SUBDIR = bfd @WITH_BFD_TRUE@BFD_LIB = bfd/libbfd.a SUBDIRS = core $(VRRP_SUBDIR) $(IPVS_SUBDIR) $(DBUS_SUBDIR) $(BFD_SUBDIR) $(TRACKER_SUBDIR) etc EXTRA_DIST = keepalived.service.in keepalived-non-root.service.in keepalived.config-opts.in keepalived_LDADD = core/libcore.a $(IPVS_LIB) $(VRRP_LIB) $(BFD_LIB) core/libcore.a $(TRACKER_LIB) ../lib/liblib.a $(KA_LIBS) MOSTLYCLEANFILES = $(am__append_1) $(am__append_2) @INIT_SYSTEMD_TRUE@systemdsystemunit_DATA = keepalived.service @INIT_SYSTEMD_TRUE@noinst_DATA = keepalived-non-root.service @INIT_OPENRC_TRUE@initdir = $(sysconfdir)/init.d @INIT_OPENRC_TRUE@init_DATA = etc/openrc/keepalived @REPRODUCIBLE_BUILD_TRUE@configdir = @DEFAULT_CONFIG_DIR@ @REPRODUCIBLE_BUILD_TRUE@config_DATA = keepalived.config-opts all: all-recursive .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files clean-sbinPROGRAMS: -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) keepalived$(EXEEXT): $(keepalived_OBJECTS) $(keepalived_DEPENDENCIES) $(EXTRA_keepalived_DEPENDENCIES) @rm -f keepalived$(EXEEXT) $(AM_V_CCLD)$(LINK) $(keepalived_OBJECTS) $(keepalived_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` install-configDATA: $(config_DATA) @$(NORMAL_INSTALL) @list='$(config_DATA)'; test -n "$(configdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(configdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(configdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(configdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(configdir)" || exit $$?; \ done uninstall-configDATA: @$(NORMAL_UNINSTALL) @list='$(config_DATA)'; test -n "$(configdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(configdir)'; $(am__uninstall_files_from_dir) install-initDATA: $(init_DATA) @$(NORMAL_INSTALL) @list='$(init_DATA)'; test -n "$(initdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(initdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(initdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(initdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(initdir)" || exit $$?; \ done uninstall-initDATA: @$(NORMAL_UNINSTALL) @list='$(init_DATA)'; test -n "$(initdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(initdir)'; $(am__uninstall_files_from_dir) install-systemdsystemunitDATA: $(systemdsystemunit_DATA) @$(NORMAL_INSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(systemdsystemunitdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(systemdsystemunitdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdsystemunitdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdsystemunitdir)" || exit $$?; \ done uninstall-systemdsystemunitDATA: @$(NORMAL_UNINSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(systemdsystemunitdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(PROGRAMS) $(DATA) $(HEADERS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(configdir)" "$(DESTDIR)$(initdir)" "$(DESTDIR)$(systemdsystemunitdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) @WITH_IPVS_FALSE@install-exec-hook: clean: clean-recursive clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am distclean: distclean-recursive -rm -f ./$(DEPDIR)/main.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-configDATA install-initDATA \ install-systemdsystemunitDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-sbinPROGRAMS @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-exec-hook install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/main.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-configDATA uninstall-initDATA \ uninstall-sbinPROGRAMS uninstall-systemdsystemunitDATA .MAKE: $(am__recursive_targets) install-am install-exec-am \ install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--depfiles check check-am clean clean-generic \ clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-configDATA install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-exec-hook \ install-html install-html-am install-info install-info-am \ install-initDATA install-man install-pdf install-pdf-am \ install-ps install-ps-am install-sbinPROGRAMS install-strip \ install-systemdsystemunitDATA installcheck installcheck-am \ installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-configDATA uninstall-initDATA \ uninstall-sbinPROGRAMS uninstall-systemdsystemunitDATA .PRECIOUS: Makefile @INIT_SYSTEMD_TRUE@keepalived.service keepalived-non-root.service: $(builddir)/Makefile @INIT_SYSTEMD_TRUE@ @rm -f $@ $@.tmp @INIT_SYSTEMD_TRUE@ @$(edit) '$(srcdir)/$@.in' >$@ @INIT_SYSTEMD_TRUE@keepalived.service: $(srcdir)/keepalived.service.in @INIT_SYSTEMD_TRUE@keepalived-non-root.service: $(srcdir)/keepalived-non-root.service.in @REPRODUCIBLE_BUILD_TRUE@keepalived.config-opts: $(builddir)/Makefile @REPRODUCIBLE_BUILD_TRUE@ @rm -f $@ $@.tmp @REPRODUCIBLE_BUILD_TRUE@ @$(edit) '$(srcdir)/$@.in' >$@ @REPRODUCIBLE_BUILD_TRUE@keepalived.config-opts: $(srcdir)/keepalived.config-opts.in # checks for realpath, and also not busybox version which does not support --relative-to @WITH_IPVS_TRUE@install-exec-hook: @WITH_IPVS_TRUE@ $(MKDIR_P) $(DESTDIR)/$(bindir) @WITH_IPVS_TRUE@ @( \ @WITH_IPVS_TRUE@ rm -f $(DESTDIR)/$(bindir)/genhash; \ @WITH_IPVS_TRUE@ realpath --relative-to=/ / >/dev/null 2>&1; \ @WITH_IPVS_TRUE@ if [ $$? -eq 0 ]; then \ @WITH_IPVS_TRUE@ $(LN_S) `realpath --relative-to="$(DESTDIR)/$(bindir)" "$(DESTDIR)/$(sbindir)/keepalived"` $(DESTDIR)/$(bindir)/genhash; \ @WITH_IPVS_TRUE@ else \ @WITH_IPVS_TRUE@ if [ $(bindir) = $(sbindir) ]; then \ @WITH_IPVS_TRUE@ d= ; \ @WITH_IPVS_TRUE@ s= ; \ @WITH_IPVS_TRUE@ else \ @WITH_IPVS_TRUE@ d=`echo $(bindir) | @SED@ -e "s:^/::"`; \ @WITH_IPVS_TRUE@ s=`echo $(sbindir) | @SED@ -e "s:^/::"`; \ @WITH_IPVS_TRUE@ \ @WITH_IPVS_TRUE@ while [ 1 ]; do \ @WITH_IPVS_TRUE@ d1=`echo $$d | @SED@ -e "s:/.*::"`; \ @WITH_IPVS_TRUE@ s1=`echo $$s | @SED@ -e "s:/.*::"`; \ @WITH_IPVS_TRUE@ if [ $$d1 != $$s1 ]; then \ @WITH_IPVS_TRUE@ break; \ @WITH_IPVS_TRUE@ fi; \ @WITH_IPVS_TRUE@ d=`echo $$d | @SED@ -e "s:^[^/]*/::"`; \ @WITH_IPVS_TRUE@ s=`echo $$s | @SED@ -e "s:^[^/]*/::"`; \ @WITH_IPVS_TRUE@ if [ -z $$d ]; then break; fi; \ @WITH_IPVS_TRUE@ if [ -z $$s ]; then break; fi; \ @WITH_IPVS_TRUE@ done; \ @WITH_IPVS_TRUE@ \ @WITH_IPVS_TRUE@ d=`echo /$$d/ | @SED@ -e "s:/[^/.]*/:/../:g" -e "s:/[^/.]*/:/../:g" -e "s:^/::"`; \ @WITH_IPVS_TRUE@ s=$$s/ ; \ @WITH_IPVS_TRUE@ fi; \ @WITH_IPVS_TRUE@ \ @WITH_IPVS_TRUE@ $(LN_S) $$d$${s}keepalived $(DESTDIR)/$(bindir)/genhash; \ @WITH_IPVS_TRUE@ fi; \ @WITH_IPVS_TRUE@ ) # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/core/0000775000175000017500000000000014772274312012445 5keepalived-2.3.3/keepalived/core/nftables.c0000664000175000017500000004340514452504706014333 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: nftables.c * * Author: Quentin Armitage, * * 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) 2020-2020 Alexandre Cassen, */ /* Up to commit 0ec6c01f this used libnftnl/libmnl, but that had overheads, * and constructing the netlink packets directly works just as well. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_NFTNL_UDATA #include #endif #include #include #include #include #include #include #include #include #include #include #include "nftables.h" #include "logger.h" #include "global_data.h" #include "list_head.h" #include "utils.h" #include "namespaces.h" /* nft supports ifnames in sets from commit 8c61fa7 (release v0.8.3, libnftnl v1.0.9 (but 0.8.2 also uses that, 0.8.4 uses v1.1.0)) */ /* nft supports concatenated ranges from commit 8ac2f3b (release v0.9.4, libnftnl v1.1.6 and kernel 5.6) */ struct mnl_socket *nl; /* lgtm [cpp/short-global-name] */ static unsigned int portid; uint32_t seq; /* lgtm [cpp/short-global-name] */ #ifdef MNL_DEBUG unsigned msg_no; #endif #ifdef _INCLUDE_UNUSED_CODE_ static int table_cb(const struct nlattr *attr, void *data) { const struct nlattr **tb = PTR_CAST_CONST(struct nlattr *, data); tb[attr->nla_type & NLA_TYPE_MASK] = attr; return MNL_CB_OK; } static void new_table(const struct nlmsghdr *nlh) { struct nlattr *tb[NFTA_TABLE_MAX+1] = {0}; struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); if (mnl_attr_parse(nlh, sizeof(*nfg), table_cb, tb) < 0) { log_message(LOG_INFO, "table parse failed"); return; } if (tb[NFTA_TABLE_NAME] && tb[NFTA_TABLE_HANDLE]) log_message(LOG_INFO, "Table %s: handle %lu", mnl_attr_get_str(tb[NFTA_TABLE_NAME]), be64toh(mnl_attr_get_u64(tb[NFTA_TABLE_HANDLE]))); } static int cb_func(const struct nlmsghdr *nlh, void *data) { if (NFNL_SUBSYS_ID(nlh->nlmsg_type) != NFNL_SUBSYS_NFTABLES) return 1; switch NFNL_MSG_TYPE(nlh->nlmsg_type) { case NFT_MSG_NEWTABLE: log_message(LOG_INFO, "%s", "NFT_MSG_NEWTABLE"); new_table(nlh);break; case NFT_MSG_NEWCHAIN: log_message(LOG_INFO, "%s", "NFT_MSG_NEWCHAIN"); break; case NFT_MSG_NEWSET: log_message(LOG_INFO, "%s", "NFT_MSG_NEWSET"); break; case NFT_MSG_NEWRULE: log_message(LOG_INFO, "%s", "NFT_MSG_NEWRULE"); break; case NFT_MSG_NEWSETELEM: log_message(LOG_INFO, "%s", "NFT_MSG_NEWSETELEM"); break; default: log_message(LOG_INFO, "Unknown msg type"); break; } return 1; } #endif #if defined HAVE_NFTNL_UDATA && !HAVE_DECL_NFTNL_UDATA_PUT_U32 static uint8_t nftnl_udata_put_u32(struct nftnl_udata_buf *buf, uint8_t type, uint32_t data) { return nftnl_udata_put(buf, type, sizeof(data), &data); } #endif static bool nl_socket_open(void) { int cur_net_namespace = -1; #if !defined _ONE_PROCESS_DEBUG_ && defined LIBIPVS_USE_NL if (prog_type == PROG_TYPE_CHECKER) { if (global_data->network_namespace_ipvs && (cur_net_namespace = set_netns_name(global_data->network_namespace_ipvs)) == -1) { log_message(LOG_INFO, "Unable to set network namespace %s", global_data->network_namespace_ipvs); return false; } } #endif nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) { log_message(LOG_INFO, "mnl_socket_open failed - %d", errno); if (cur_net_namespace >= 0) restore_net_namespace(cur_net_namespace); return false; } if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { log_message(LOG_INFO, "mnl_socket_bind error - %d", errno); mnl_socket_close(nl); nl = NULL; if (cur_net_namespace >= 0) restore_net_namespace(cur_net_namespace); return false; } portid = mnl_socket_get_portid(nl); if (cur_net_namespace >= 0) restore_net_namespace(cur_net_namespace); return true; } static void exchange_nl_msg(struct mnl_nlmsg_batch *batch) { int ret; int ret_cb; char *buf; size_t buf_size; long mnl_buf_size; int sav_errno = errno; if (mnl_nlmsg_batch_is_empty(batch)) return; #ifdef MNL_DEBUG FILE *fp = NULL; const char *file_name; if (prog_type == PROG_TYPE_VRRP) { file_name = make_tmp_filename("nftrace"); fp = fopen(file_name, "a"); FREE_CONST(file_name); unsigned char *p = mnl_nlmsg_batch_head(batch); size_t i; fprintf(fp, "mnl_nlmsg_batch_size (%u), %zu\n", msg_no, mnl_nlmsg_batch_size(batch)); log_message(LOG_INFO, "exchange_nl_msg (%u), len %zu", msg_no, mnl_nlmsg_batch_size(batch)); for (i = 0; i < mnl_nlmsg_batch_size(batch); i++, p++) { if (!(i % 16)) fprintf(fp, "%4.4zx: ", i); fprintf(fp, " %2.2x", *p); if (i % 16 == 15) fprintf(fp, "\n"); } if (i % 16) fprintf(fp, "\n"); mnl_nlmsg_fprintf(fp, PTR_CAST(char, mnl_nlmsg_batch_head(batch)), mnl_nlmsg_batch_size(batch), sizeof( struct nfgenmsg)); fflush(fp); } #endif if (!nl && !nl_socket_open()) return; if (mnl_socket_sendto(nl, mnl_nlmsg_batch_head(batch), mnl_nlmsg_batch_size(batch)) < 0) { log_message(LOG_INFO, "mnl_socket_send error - %d", errno); return; } mnl_buf_size = MNL_SOCKET_BUFFER_SIZE; if (mnl_buf_size < 1) buf_size = 8192L; else buf_size = (size_t)mnl_buf_size; buf = MALLOC(buf_size); ret_cb = 1; while ((ret = mnl_socket_recvfrom(nl, buf, buf_size)) > 0) { #ifdef MNL_DEBUG if (fp) { log_message(LOG_INFO, "mnl_socket_recvfrom (%u) returned %d", msg_no, ret); fprintf(fp, "\n\nReply %u\n\n", msg_no++); mnl_nlmsg_fprintf(fp, buf, ret, sizeof( struct nfgenmsg)); fflush(fp); } #endif ret_cb = mnl_cb_run(buf, ret, 0, portid, NULL, NULL); if (ret_cb <= 0) break; } sav_errno = errno; #ifdef MNL_DEBUG if (fp) fclose(fp); #endif FREE(buf); if (ret == -1 || ret_cb < 0) { errno = sav_errno; log_message(LOG_INFO, "mnl_socket_recvfrom error ret %d - errno %d (%m), ret_cb %d,", ret, errno, ret_cb); } } #ifdef _WITH_VRRP_ void exchange_nl_msg_single(struct nlmsghdr *nlm, int (*cb_func)(const struct nlmsghdr *, void *), bool *success) { int ret; char buf[256]; #ifdef MNL_DEBUG const char *filename; FILE *fp; filename = make_tmp_filename("nftrace"); fp = fopen(filename, "a"); FREE_CONST(filename); mnl_nlmsg_fprintf(fp, PTR_CAST(char, nlm), nlm->nlmsg_len, 0); fclose(fp); #endif if (!nl && !nl_socket_open()) return; if (mnl_socket_sendto(nl, nlm, nlm->nlmsg_len) < 0) { log_message(LOG_INFO, "mnl_socket_send error - %d", errno); return ; } *success = false; while ((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) { ret = mnl_cb_run(buf, ret, 0, portid, cb_func, success); if (ret <= 0) break; } if (ret == -1 && errno != ENOENT) log_message(LOG_INFO, "mnl_socket_recvfrom single error - %d", errno); } #endif void my_mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *batch) { if (!mnl_nlmsg_batch_next(batch)) { exchange_nl_msg(batch); mnl_nlmsg_batch_reset(batch); } } void add_payload(struct nftnl_rule *r, uint32_t base, uint32_t dreg, uint32_t offset, uint32_t len) { struct nftnl_expr *e; e = nftnl_expr_alloc("payload"); if (e == NULL) { log_message(LOG_INFO, "expr payload oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base); nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg); nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset); nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len); nftnl_rule_add_expr(r, e); } void add_meta(struct nftnl_rule *r, uint32_t ifindex, uint32_t dreg) { struct nftnl_expr *e; e = nftnl_expr_alloc("meta"); if (e == NULL) { log_message(LOG_INFO, "expr payload oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTNL_EXPR_META_DREG, dreg); nftnl_expr_set_u32(e, NFTNL_EXPR_META_KEY, ifindex); nftnl_rule_add_expr(r, e); } #ifdef _WITH_LVS_ void add_meta_sreg(struct nftnl_rule *r, uint32_t ifindex, uint32_t sreg) { struct nftnl_expr *e; e = nftnl_expr_alloc("meta"); if (e == NULL) { log_message(LOG_INFO, "expr payload oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTNL_EXPR_META_SREG, sreg); nftnl_expr_set_u32(e, NFTNL_EXPR_META_KEY, ifindex); nftnl_rule_add_expr(r, e); } #endif void add_lookup(struct nftnl_rule *r, uint32_t base, uint32_t dreg, const char *set_name, uint32_t set_id, #ifndef HAVE_NFTNL_EXPR_LOOKUP_FLAG_INV __attribute__((unused)) #endif bool neg) { struct nftnl_expr *e; e = nftnl_expr_alloc("lookup"); if (e == NULL) { log_message(LOG_INFO, "expr lookup oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SREG, base); if (dreg != NO_REG) nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_DREG, dreg); #ifdef HAVE_NFTNL_EXPR_LOOKUP_FLAG_INV if (neg) nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_FLAGS, NFT_LOOKUP_F_INV); #endif nftnl_expr_set_str(e, NFTNL_EXPR_LOOKUP_SET, set_name); if (set_id) nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SET_ID, set_id); nftnl_rule_add_expr(r, e); } #if defined _WITH_VRRP_ && HAVE_DECL_NFTA_DUP_MAX && defined _HAVE_VRRP_VMAC_ void add_dup(struct nftnl_rule *r, uint32_t addr_reg, uint32_t dev_reg) { struct nftnl_expr *e; e = nftnl_expr_alloc("dup"); if (e == NULL) { log_message(LOG_INFO, "dup payload oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTNL_EXPR_DUP_SREG_ADDR, addr_reg); nftnl_expr_set_u32(e, NFTNL_EXPR_DUP_SREG_DEV, dev_reg); nftnl_rule_add_expr(r, e); } #endif /* verdict should be NF_DROP, NF_ACCEPT, NFT_RETURN, ... */ /* "The nf_tables verdicts share their numeric space with the netfilter verdicts." */ void add_immediate_verdict(struct nftnl_rule *r, uint32_t verdict, const char *chain) { struct nftnl_expr *e; e = nftnl_expr_alloc("immediate"); if (e == NULL) { log_message(LOG_INFO, "expr immediate oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, NFT_REG_VERDICT); if (chain) nftnl_expr_set_str(e, NFTNL_EXPR_IMM_CHAIN, chain); nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_VERDICT, verdict); nftnl_rule_add_expr(r, e); } #ifdef _INCLUDE_UNUSED_CODE_ static void add_immediate_data(struct nftnl_rule *r, uint32_t reg, const void *data, uint32_t data_len) { struct nftnl_expr *e; e = nftnl_expr_alloc("immediate"); if (e == NULL) { log_message(LOG_INFO, "expr immediate oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTNL_EXPR_IMM_DREG, reg); nftnl_expr_set_data(e, NFTNL_EXPR_IMM_DATA, data, data_len); nftnl_rule_add_expr(r, e); } #endif void add_cmp(struct nftnl_rule *r, uint32_t sreg, uint32_t op, const void *data, uint32_t data_len) { struct nftnl_expr *e; e = nftnl_expr_alloc("cmp"); if (e == NULL) { log_message(LOG_INFO, "expr cmp oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_SREG, sreg); nftnl_expr_set_u32(e, NFTNL_EXPR_CMP_OP, op); nftnl_expr_set_data(e, NFTNL_EXPR_CMP_DATA, data, data_len); nftnl_rule_add_expr(r, e); } #ifdef _WITH_VRRP_ void add_bitwise(struct nftnl_rule *r, uint32_t sreg, uint32_t dreg, uint32_t len, const void *mask, const void *xor) { struct nftnl_expr *e; e = nftnl_expr_alloc("bitwise"); if (e == NULL) { log_message(LOG_INFO, "expr cmp oom error - %d", errno); return; } nftnl_expr_set_u32(e, NFTA_BITWISE_SREG, sreg); nftnl_expr_set_u32(e, NFTA_BITWISE_DREG, dreg); nftnl_expr_set_u32(e, NFTA_BITWISE_LEN, len); nftnl_expr_set_data(e, NFTA_BITWISE_MASK, mask, len); nftnl_expr_set_data(e, NFTA_BITWISE_XOR, xor, len); nftnl_rule_add_expr(r, e); } #endif void add_counter(struct nftnl_rule *r) { struct nftnl_expr *e; if (!global_data->nf_counters) return; e = nftnl_expr_alloc("counter"); if (e == NULL) { log_message(LOG_INFO, "expr counter oom error - %d", errno); return; } nftnl_rule_add_expr(r, e); } struct nftnl_table * table_add_parse(uint16_t family, const char *table) { struct nftnl_table *t; t = nftnl_table_alloc(); if (t == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_table_set_u32(t, NFTNL_TABLE_FAMILY, family); nftnl_table_set_str(t, NFTNL_TABLE_NAME, table); return t; } struct nftnl_chain * chain_add_parse(const char *table, const char *name) { struct nftnl_chain *t; t = nftnl_chain_alloc(); if (t == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_chain_set_str(t, NFTNL_CHAIN_TABLE, table); nftnl_chain_set_str(t, NFTNL_CHAIN_NAME, name); return t; } /* For an anonymous set use set name "__set%d", and retrieve set_id with: set_id = nftnl_set_get_u32(s, NFTNL_SET_ID); * * To add a rule referencing the set, setname is "__set%d", and set set_id: if (set_id) nftnl_expr_set_u32(e, NFTNL_EXPR_LOOKUP_SET_ID, set_id); * It works similarly for maps */ struct nftnl_set *setup_set(uint8_t family, const char *table, const char *name, int type, int set_type, int data_type) { struct nftnl_set *s = NULL; #ifdef HAVE_NFTNL_UDATA struct nftnl_udata_buf *udbuf; #endif static int set_id = 0; int type_copy = type; int size = 0; int data_size = 0; s = nftnl_set_alloc(); if (s == NULL) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } while (type_copy) { switch (type_copy & NFT_TYPE_MASK) { case NFT_TYPE_IPADDR: size += sizeof(struct in_addr); break; case NFT_TYPE_IP6ADDR: size += sizeof(struct in6_addr); break; case NFT_TYPE_IFINDEX: case NFT_TYPE_INET_SERVICE: size += sizeof(uint32_t); break; case NFT_TYPE_ICMPV6_TYPE: size++; break; case NFT_TYPE_IFNAME: case NFT_TYPE_STRING: /* Used if nft doesn't support ifname type */ size += IFNAMSIZ; break; default: log_message(LOG_INFO, "Unsupported type %d\n", type_copy & NFT_TYPE_MASK); break; } type_copy >>= NFT_TYPE_BITS; } if (set_type & NFT_SET_MAP) { switch (data_type) { case NFT_TYPE_IPADDR: data_size = sizeof(struct in_addr); break; case NFT_TYPE_IP6ADDR: data_size = sizeof(struct in6_addr); break; case NFT_TYPE_IFINDEX: case NFT_TYPE_MARK: data_size = sizeof(uint32_t); break; case NFT_TYPE_ICMPV6_TYPE: data_size = 1; break; case NFT_TYPE_IFNAME: data_size = IFNAMSIZ; break; default: log_message(LOG_INFO, "Unsupported type %d\n", data_type); break; } } nftnl_set_set_str(s, NFTNL_SET_TABLE, table); nftnl_set_set_str(s, NFTNL_SET_NAME, name); nftnl_set_set_u32(s, NFTNL_SET_FAMILY, family); nftnl_set_set_u32(s, NFTNL_SET_KEY_LEN, size); /* inet service type, see nftables/include/datatypes.h */ nftnl_set_set_u32(s, NFTNL_SET_KEY_TYPE, type); if (set_type & NFT_SET_MAP) { nftnl_set_set_u32(s, NFTNL_SET_FLAGS, set_type); nftnl_set_set_u32(s, NFTNL_SET_DATA_TYPE, data_type); nftnl_set_set_u32(s, NFTNL_SET_DATA_LEN, data_size); } nftnl_set_set_u32(s, NFTNL_SET_ID, ++set_id); #ifdef HAVE_NFTNL_UDATA udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); if (!udbuf) { log_message(LOG_INFO, "OOM error - %d", errno); return NULL; } nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEYBYTEORDER, type == NFT_TYPE_IPADDR || type == NFT_TYPE_IP6ADDR ? BYTEORDER_BIG_ENDIAN : BYTEORDER_HOST_ENDIAN); if (set_type & NFT_SET_MAP) nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_DATABYTEORDER, BYTEORDER_HOST_ENDIAN); nftnl_set_set_data(s, NFTNL_SET_USERDATA, nftnl_udata_buf_data(udbuf), nftnl_udata_buf_len(udbuf)); nftnl_udata_buf_free(udbuf); #endif return s; } struct mnl_nlmsg_batch * nft_start_batch(void) { struct mnl_nlmsg_batch *batch; char *buf = MALLOC(2 * MNL_SOCKET_BUFFER_SIZE); time_t time_ret; if (!seq) { time_ret = time(NULL); if (time_ret == -1) seq = 1; else seq = (uint32_t)time_ret; } batch = mnl_nlmsg_batch_start(buf, 2 * MNL_SOCKET_BUFFER_SIZE); nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); my_mnl_nlmsg_batch_next(batch); return batch; } void nft_end_batch(struct mnl_nlmsg_batch *batch, bool more) { void *buf; nftnl_batch_end(mnl_nlmsg_batch_current(batch), seq++); my_mnl_nlmsg_batch_next(batch); exchange_nl_msg(batch); if (more) { mnl_nlmsg_batch_reset(batch); nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); my_mnl_nlmsg_batch_next(batch); } else { buf = mnl_nlmsg_batch_head(batch); FREE(buf); mnl_nlmsg_batch_stop(batch); } } void nft_discard_batch(struct mnl_nlmsg_batch *batch) { FREE(batch); } #ifdef _WITH_VRRP_ int set_nf_ifname_type(void) { FILE *fp; char nft_ver_buf[64]; char *p; unsigned nft_major = 0, nft_minor = 0, nft_release = 0; unsigned nft_version = 0; int ifname_type; fp = popen("nft -v 2>/dev/null", "r"); if (fp) { if (fgets(nft_ver_buf, sizeof(nft_ver_buf), fp)) { if (!(p = strchr(nft_ver_buf, ' '))) p = nft_ver_buf; while (*p == ' ') p++; if (*p == 'v') p++; if (sscanf(p, "%u.%u.%u", &nft_major, &nft_minor, &nft_release) >= 2) nft_version = (nft_major * 0x100 + nft_minor) * 0x100 + nft_release; } pclose(fp); } if (nft_version) ifname_type = nft_version >= 0x000803 ? NFT_TYPE_IFNAME : NFT_TYPE_STRING; else ifname_type = LIBNFTNL_VERSION > 0x010009 ? NFT_TYPE_IFNAME : NFT_TYPE_STRING; return ifname_type; } #endif keepalived-2.3.3/keepalived/core/global_data.c0000664000175000017500000010335414756615757015006 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Dynamic data structure definition. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include "global_data.h" #include "list_head.h" #include "logger.h" #include "parser.h" #include "utils.h" #include "main.h" #include "memory.h" #ifdef _WITH_VRRP_ #include "vrrp.h" #include "vrrp_ipaddress.h" #endif #include "process.h" #ifdef _WITH_FIREWALL_ #include "vrrp_firewall.h" #endif #include "align.h" #include "pidfile.h" #ifdef _WITH_JSON_ #include "global_json.h" #endif #ifdef _WITH_DBUS_ #include "vrrp_dbus.h" #endif /* global vars */ data_t *global_data = NULL; data_t *old_global_data = NULL; /* Default settings */ static void set_default_router_id(data_t *data, const char *new_id) { if (!new_id || !new_id[0]) return; data->router_id = STRDUP(new_id); } static void set_default_email_from(data_t * data, const char *hostname) { struct passwd *pwd = NULL; size_t len; char *str; if (!hostname || !hostname[0]) return; pwd = getpwuid(getuid()); if (!pwd) return; len = strlen(hostname) + strlen(pwd->pw_name) + 2; data->email_from = str = MALLOC(len); if (!data->email_from) return; snprintf(str, len, "%s@%s", pwd->pw_name, hostname); } static void set_default_smtp_connection_timeout(data_t * data) { data->smtp_connection_to = DEFAULT_SMTP_CONNECTION_TIMEOUT; } #ifdef _WITH_VRRP_ static void set_default_mcast_group(data_t * data) { /* coverity[check_return] */ inet_stosockaddr(INADDR_VRRP_GROUP, NULL, PTR_CAST(sockaddr_t, &data->vrrp_mcast_group4)); /* coverity[check_return] */ inet_stosockaddr(INADDR6_VRRP_GROUP, NULL, PTR_CAST(sockaddr_t, &data->vrrp_mcast_group6)); } static void set_vrrp_defaults(data_t * data) { data->vrrp_garp_rep = VRRP_GARP_REP; data->vrrp_garp_refresh.tv_sec = VRRP_GARP_REFRESH; data->vrrp_garp_refresh_rep = VRRP_GARP_REFRESH_REP; data->vrrp_garp_delay = VRRP_GARP_DELAY; data->vrrp_garp_lower_prio_delay = PARAMETER_UNSET; data->vrrp_garp_lower_prio_rep = PARAMETER_UNSET; data->vrrp_down_timer_adverts = VRRP_DOWN_TIMER_ADVERTS; #ifdef _HAVE_VRRP_VMAC_ data->vrrp_vmac_garp_intvl = 0; #endif data->vrrp_lower_prio_no_advert = false; data->vrrp_higher_prio_send_advert = false; data->vrrp_version = VRRP_VERSION_2; #ifdef _HAVE_LIBIPSET_ data->using_ipsets = PARAMETER_UNSET; #endif data->vrrp_check_unicast_src = false; data->vrrp_skip_check_adv_addr = false; data->vrrp_strict = false; #ifdef _WITH_NFTABLES_ data->vrrp_nf_chain_priority = -1; #endif } #endif /* email facility functions */ static void free_email_list(list_head_t *l) { email_t *email, *email_tmp; list_for_each_entry_safe(email, email_tmp, l, e_list) { FREE_CONST_PTR(email->addr); FREE(email); } } static void dump_email_list(FILE *fp, const list_head_t *l) { email_t *email; list_for_each_entry(email, l, e_list) conf_write(fp, " %s", email->addr); } const char * format_email_addr(const char *addr) { char *new_addr; size_t len = strlen(addr); const char *end_description; const char *quote_char; unsigned num_esc; const char *ip; char *op; if (addr[len - 1] != '>') return STRDUP(addr); if (!(end_description = strrchr(addr, '<'))) { /* We don't have a starting < - at the moment log it and copy verbatim */ log_message(LOG_INFO, "email address '%s' invalid", addr); return STRDUP(addr); } /* Skip over white-space before < */ end_description--; while (end_description > addr && (*end_description == ' ' || *end_description == '\t')) end_description--; /* We can't have a '"' because alloc_strvec_r() doesn't support it. * We might be able to use alloc_strvec_quoted_escaped(), in which * case we probably can have embedded '"'s. */ /* Do we have any of the characters that need quoting - see RFC5322 3.2.3? */ quote_char = strpbrk(addr, "()<>[]:;@\\,."); if (!quote_char || quote_char > end_description) return STRDUP(addr); /* We need to quote any embedded '"'s or '\'s */ quote_char = addr; num_esc = 0; while ((quote_char = strpbrk(quote_char, "\"\\")) && quote_char <= end_description) { num_esc++; quote_char++; } new_addr = MALLOC(len + 2 + num_esc + 1); ip = addr; op = new_addr; *op++ = '"'; while ((quote_char = strpbrk(ip, "\"\\")) && quote_char <= end_description) { strncpy(op, ip, quote_char - ip); op += quote_char - ip; *op++ = '\\'; *op++ = *quote_char++; ip = quote_char; } sprintf(op, "%.*s\"%s", (int)(end_description - ip + 1), ip, end_description + 1); return new_addr; } void alloc_email(const char *addr) { email_t *email; PMALLOC(email); INIT_LIST_HEAD(&email->e_list); email->addr = format_email_addr(addr); list_add_tail(&email->e_list, &global_data->email); } /* data facility functions */ data_t * alloc_global_data(void) { data_t *new; if (global_data) return global_data; PMALLOC(new); INIT_LIST_HEAD(&new->email); new->smtp_alert = -1; #ifdef _WITH_VRRP_ new->smtp_alert_vrrp = -1; #endif #ifdef _WITH_LVS_ new->smtp_alert_checker = -1; #endif #ifdef _WITH_VRRP_ set_default_mcast_group(new); set_vrrp_defaults(new); #endif new->notify_fifo.fd = -1; new->max_auto_priority = 0; new->min_auto_priority_delay = 1000000; /* 1 second */ #ifdef _WITH_VRRP_ new->vrrp_notify_fifo.fd = -1; new->vrrp_rlimit_rt = RT_RLIMIT_DEFAULT; new->vrrp_rx_bufs_multiples = 3; #endif #ifdef _WITH_LVS_ new->lvs_notify_fifo.fd = -1; new->checker_rlimit_rt = RT_RLIMIT_DEFAULT; #ifdef _WITH_BFD_ new->bfd_rlimit_rt = RT_RLIMIT_DEFAULT; #endif #endif #ifdef _WITH_SNMP_ if (snmp_option) { #ifdef _WITH_SNMP_VRRP_ new->enable_snmp_vrrp = true; #endif #ifdef _WITH_SNMP_RFCV2_ new->enable_snmp_rfcv2 = true; #endif #ifdef _WITH_SNMP_RFCV3_ new->enable_snmp_rfcv3 = true; #endif #ifdef _WITH_SNMP_CHECKER_ new->enable_snmp_checker = true; #endif } if (snmp_socket) new->snmp_socket = STRDUP(snmp_socket); #ifdef _WITH_SNMP_CHECKER_ new->snmp_vs_stats_update_interval = 5 * TIMER_HZ; /* 5 seconds */ new->snmp_rs_stats_update_interval = 0; #endif #endif #ifdef _WITH_LVS_ #ifdef _WITH_VRRP_ new->lvs_syncd.syncid = PARAMETER_UNSET; #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ new->lvs_syncd.mcast_group.ss_family = AF_UNSPEC; #endif #endif #endif #ifdef _WITH_JSON_ new->json_version = JSON_VERSION_V1; #endif return new; } void init_global_data(data_t * data, data_t *prev_global_data, bool copy_unchangeable_config) { /* If this is a reload and we are running in a network namespace, * we may not be able to get local_name, so preserve it */ const char unknown_name[] = "[unknown]"; /* If we are running in a network namespace, we may not be * able to get our local name now, so re-use original */ if (prev_global_data) { data->local_name = prev_global_data->local_name; prev_global_data->local_name = NULL; if (copy_unchangeable_config) { FREE_CONST_PTR(data->network_namespace); data->network_namespace = prev_global_data->network_namespace; prev_global_data->network_namespace = NULL; FREE_CONST_PTR(data->network_namespace_ipvs); data->network_namespace_ipvs = prev_global_data->network_namespace_ipvs; prev_global_data->network_namespace_ipvs = NULL; FREE_CONST_PTR(data->instance_name); data->instance_name = prev_global_data->instance_name; prev_global_data->instance_name = NULL; } } #ifndef _ONE_PROCESS_DEBUG_ if (data->reload_file == DEFAULT_RELOAD_FILE) { if (data->instance_name) data->reload_file = make_pidfile_name(KEEPALIVED_PID_DIR KEEPALIVED_PID_FILE, data->instance_name, RELOAD_EXTENSION); else if (use_pid_dir) data->reload_file = STRDUP(KEEPALIVED_PID_DIR KEEPALIVED_PID_FILE RELOAD_EXTENSION); else data->reload_file = STRDUP(RUNSTATEDIR "/" KEEPALIVED_PID_FILE RELOAD_EXTENSION); } #endif if (!data->local_name && (!data->router_id || (data->smtp_server.ss_family && (!data->smtp_helo_name || !data->email_from)))) { data->local_name = get_local_name(); /* If for some reason get_local_name() fails, we need to have * some string in local_name, otherwise keepalived can segfault */ if (!data->local_name) data->local_name = STRDUP(unknown_name); } if (!data->router_id) set_default_router_id(data, data->local_name); if (data->smtp_server.ss_family) { if (!data->smtp_connection_to) set_default_smtp_connection_timeout(data); if (data->local_name && strcmp(data->local_name, unknown_name)) { if (!data->email_from) set_default_email_from(data, data->local_name); if (!data->smtp_helo_name) data->smtp_helo_name = STRDUP(data->local_name); } } set_symlinks(global_data->use_symlinks); /* Check that there aren't conflicts with the notify FIFOs */ #ifdef _WITH_VRRP_ /* If the global and vrrp notify FIFOs are the same, then data will be * duplicated on the FIFO */ if ( #ifndef _ONE_PROCESS_DEBUG_ prog_type == PROG_TYPE_VRRP && #endif data->notify_fifo.name && data->vrrp_notify_fifo.name && !strcmp(data->notify_fifo.name, data->vrrp_notify_fifo.name)) { log_message(LOG_INFO, "notify FIFO %s has been specified for global and vrrp FIFO - ignoring vrrp FIFO", data->vrrp_notify_fifo.name); FREE_CONST_PTR(data->vrrp_notify_fifo.name); data->vrrp_notify_fifo.name = NULL; free_notify_script(&data->vrrp_notify_fifo.script); } #endif #ifdef _WITH_LVS_ /* If the global and LVS notify FIFOs are the same, then data will be * duplicated on the FIFO */ #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_CHECKER) #endif { if (data->notify_fifo.name && data->lvs_notify_fifo.name && !strcmp(data->notify_fifo.name, data->lvs_notify_fifo.name)) { log_message(LOG_INFO, "notify FIFO %s has been specified for global and LVS FIFO - ignoring LVS FIFO", data->lvs_notify_fifo.name); FREE_CONST_PTR(data->lvs_notify_fifo.name); data->lvs_notify_fifo.name = NULL; free_notify_script(&data->lvs_notify_fifo.script); } #ifdef _WITH_VRRP_ /* If LVS and VRRP use the same FIFO, they cannot both have a script for the FIFO. * Use the VRRP script and ignore the LVS script */ if (data->lvs_notify_fifo.name && data->vrrp_notify_fifo.name && !strcmp(data->lvs_notify_fifo.name, data->vrrp_notify_fifo.name) && data->lvs_notify_fifo.script && data->vrrp_notify_fifo.script) { log_message(LOG_INFO, "LVS notify FIFO and vrrp FIFO are the same both with scripts - ignoring LVS FIFO script"); free_notify_script(&data->lvs_notify_fifo.script); } #endif } #ifdef _WITH_SNMP_CHECKER_ if (!data->snmp_rs_stats_update_interval) 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 free_global_data(data_t **datap) { data_t *data = *datap; if (!data) return; free_email_list(&data->email); FREE_CONST_PTR(data->network_namespace); FREE_CONST_PTR(data->network_namespace_ipvs); FREE_CONST_PTR(data->instance_name); FREE_CONST_PTR(data->process_name); #ifdef _WITH_VRRP_ FREE_CONST_PTR(data->vrrp_process_name); #endif #ifdef _WITH_LVS_ FREE_CONST_PTR(data->lvs_process_name); #endif #ifdef _WITH_BFD_ FREE_CONST_PTR(data->bfd_process_name); #endif FREE_CONST_PTR(data->router_id); FREE_CONST_PTR(data->email_from); FREE_CONST_PTR(data->smtp_helo_name); FREE_CONST_PTR(data->local_name); #ifdef _WITH_SNMP_ FREE_CONST_PTR(data->snmp_socket); #endif free_notify_script(&data->startup_script); free_notify_script(&data->shutdown_script); #if defined _WITH_LVS_ && defined _WITH_VRRP_ FREE_CONST_PTR(data->lvs_syncd.ifname); FREE_CONST_PTR(data->lvs_syncd.vrrp_name); #endif FREE_CONST_PTR(data->notify_fifo.name); free_notify_script(&data->notify_fifo.script); #ifdef _WITH_VRRP_ FREE_CONST_PTR(data->default_ifname); FREE_CONST_PTR(data->vrrp_notify_fifo.name); free_notify_script(&data->vrrp_notify_fifo.script); #ifdef _HAVE_VRRP_VMAC_ FREE_CONST_PTR(data->vmac_prefix); FREE_CONST_PTR(data->vmac_addr_prefix); #endif #ifdef _WITH_IPTABLES_ FREE_CONST_PTR(data->vrrp_iptables_inchain); FREE_CONST_PTR(data->vrrp_iptables_outchain); #ifdef _HAVE_LIBIPSET_ FREE_CONST_PTR(data->vrrp_ipset_address); FREE_CONST_PTR(data->vrrp_ipset_address6); FREE_CONST_PTR(data->vrrp_ipset_address_iface6); FREE_CONST_PTR(data->vrrp_ipset_igmp); FREE_CONST_PTR(data->vrrp_ipset_mld); #ifdef _HAVE_VRRP_VMAC_ FREE_CONST_PTR(data->vrrp_ipset_vmac_nd); #endif #endif #endif #ifdef _WITH_NFTABLES_ FREE_CONST_PTR(data->vrrp_nf_table_name); #endif #endif #ifdef _WITH_LVS_ FREE_CONST_PTR(data->lvs_notify_fifo.name); free_notify_script(&data->lvs_notify_fifo.script); #ifdef _WITH_NFTABLES_ FREE_CONST_PTR(data->ipvs_nf_table_name); #endif #endif #ifdef _WITH_DBUS_ FREE_CONST_PTR(data->dbus_service_name); FREE_CONST_PTR(data->dbus_no_interface_name); #endif #ifndef _ONE_PROCESS_DEBUG_ FREE_CONST_PTR(data->reload_check_config); FREE_CONST_PTR(data->reload_file); 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_CONST_PTR(data->state_dump_file); FREE_CONST_PTR(data->stats_dump_file); FREE_CONST_PTR(data->json_dump_file); FREE(data); *datap = NULL; } FILE * __attribute__((malloc)) open_dump_file(const char *default_file_name) { FILE *fp; const char *file_name; char *full_file_name; const char *dot; size_t len; const char *dir; size_t dir_len; /* * If no leading /, use tmp_dir * If trailing /, add "keepalived%s.data", default_file_name */ if (global_data->state_dump_file && global_data->state_dump_file[0] == '/') { dir = global_data->state_dump_file; dir_len = strlen(dir); if (dir[dir_len - 1] != '/') dir_len = strrchr(dir, '/') - dir; } else { dir = tmp_dir; dir_len = strlen(tmp_dir); } if (global_data->state_dump_file && global_data->state_dump_file[strlen(global_data->state_dump_file) - 1] != '/') { if (!(file_name = strrchr(global_data->state_dump_file, '/'))) file_name = global_data->state_dump_file; else file_name++; /* Skip to last '/' */ } else file_name = "keepalived.data"; if (!(dot = strrchr(file_name, '.'))) dot = file_name + strlen(file_name); len = dir_len + 1 + strlen(file_name) + 1 + strlen(default_file_name); if (global_data->data_use_instance) { if (global_data->instance_name) len += strlen(global_data->instance_name) + 1; if (global_data->network_namespace) len += strlen(global_data->network_namespace) + 1; } full_file_name = MALLOC(len); snprintf(full_file_name, len, "%.*s/%.*s%s%s%s%s%s%s", (int)dir_len, dir, (int)(dot - file_name), file_name, default_file_name, global_data->data_use_instance && (global_data->instance_name || global_data->network_namespace) ? "." : "", global_data->data_use_instance && global_data->network_namespace ? global_data->network_namespace : "", global_data->data_use_instance && global_data->instance_name && global_data->network_namespace ? "_" : "", global_data->data_use_instance && global_data->instance_name ? global_data->instance_name : "", dot); fp = fopen_safe(full_file_name, "w"); if (!fp) log_message(LOG_INFO, "Can't open dump file %s (%d: %s)", full_file_name, errno, strerror(errno)); FREE_CONST(full_file_name); return fp; } static void write_fifo_details(FILE *fp, const notify_fifo_t *fifo, const char *type) { conf_write(fp, " %s notify fifo = %s, uid:gid %u:%u", type, fifo->name, fifo->uid, fifo->gid); if (!fifo->script) return; if (fifo->script->path) conf_write(fp, " %s notify fifo path = %s, script = %s, uid:gid %u:%u", type, fifo->script->path, cmd_str(fifo->script), fifo->script->uid, fifo->script->gid); else conf_write(fp, " %s notify fifo script = %s, uid:gid %u:%u", type, cmd_str(fifo->script), fifo->script->uid, fifo->script->gid); } void dump_global_data(FILE *fp, data_t * data) { char cpu_str[64]; #ifdef _WITH_VRRP_ char buf[64]; #endif #ifndef _ONE_PROCESS_DEBUG_ char date_time_str[20]; struct tm tm; #endif unsigned val; uid_t uid; gid_t gid; if (!data) return; conf_write(fp, "------< Global definitions >------"); #ifndef _ONE_PROCESS_DEBUG_ if (config_save_dir) conf_write(fp, " Config save dir = %s", config_save_dir); #endif conf_write(fp, " Network namespace = %s", data->network_namespace ? data->network_namespace : "(default)"); conf_write(fp, " Network namespace ipvs = %s", data->network_namespace_ipvs ? data->network_namespace_ipvs[0] ? data->network_namespace_ipvs : "(default)" : "(main namespace)"); if (data->instance_name) conf_write(fp, " Instance name = %s", data->instance_name); if (data->process_name) conf_write(fp, " Parent process name = %s", data->process_name); #ifdef _WITH_VRRP_ if (data->vrrp_process_name) conf_write(fp, " VRRP process name = %s", data->vrrp_process_name); #endif #ifdef _WITH_LVS_ if (data->lvs_process_name) conf_write(fp, " LVS process name = %s", data->lvs_process_name); #endif #ifdef _WITH_BFD_ if (data->bfd_process_name) conf_write(fp, " BFD process name = %s", data->bfd_process_name); #endif conf_write(fp, " %s symlinks in script paths", data->use_symlinks ? "Keep" : "Replace"); if (data->router_id) conf_write(fp, " Router ID = %s", data->router_id); if (data->smtp_server.ss_family) { conf_write(fp, " Smtp server = %s", inet_sockaddrtos(&data->smtp_server)); conf_write(fp, " Smtp server port = %u", ntohs(inet_sockaddrport(&data->smtp_server))); } if (data->smtp_helo_name) conf_write(fp, " Smtp HELO name = %s" , data->smtp_helo_name); if (data->smtp_connection_to) conf_write(fp, " Smtp server connection timeout = %lu" , data->smtp_connection_to / TIMER_HZ); if (data->email_from) { conf_write(fp, " Email notification from = %s" , data->email_from); conf_write(fp, " Email notification to:"); dump_email_list(fp, &data->email); } conf_write(fp, " Default smtp_alert = %s", data->smtp_alert == -1 ? "unset" : data->smtp_alert ? "on" : "off"); #ifdef _WITH_VRRP_ conf_write(fp, " Default smtp_alert_vrrp = %s", data->smtp_alert_vrrp == -1 ? "unset" : data->smtp_alert_vrrp ? "on" : "off"); #endif #ifdef _WITH_LVS_ conf_write(fp, " Default smtp_alert_checker = %s", data->smtp_alert_checker == -1 ? "unset" : data->smtp_alert_checker ? "on" : "off"); conf_write(fp, " Checkers log all failures = %s", data->checker_log_all_failures ? "true" : "false"); #endif #ifndef _ONE_PROCESS_DEBUG_ if (data->reload_check_config) conf_write(fp, " Test config before reload, log to %s", data->reload_check_config); else conf_write(fp, " No test config before reload"); if (data->reload_time_file) { conf_write(fp, " Reload time file = %s%s", data->reload_time_file, data->reload_repeat ? " (repeat)" : ""); if (data->reload_time) { localtime_r(&data->reload_time, &tm); strftime(date_time_str, sizeof(date_time_str), "%Y-%m-%d %H:%M:%S", &tm); conf_write(fp, " Reload scheduled for %s%s", date_time_str, global_data->reload_date_specified ? " (date specified" : ""); } else conf_write(fp, " No reload scheduled"); } if (data->reload_file) conf_write(fp, " Reload_file = %s", data->reload_file); #endif conf_write(fp, " keep script symlinks = %s", data->use_symlinks ? "true" : "false"); if (data->config_directory) conf_write(fp, " config save directory = %s", data->config_directory); if (data->data_use_instance) conf_write(fp, " Use instance name in data dumps"); if (data->startup_script) conf_write(fp, " Startup script = %s, uid:gid %u:%u, timeout %u", cmd_str(data->startup_script), data->startup_script->uid, data->startup_script->gid, data->startup_script_timeout); if (data->shutdown_script) conf_write(fp, " Shutdown script = %s, uid:gid %u:%u timeout %u", cmd_str(data->shutdown_script), data->shutdown_script->uid, data->shutdown_script->gid, data->shutdown_script_timeout); #ifdef _WITH_VRRP_ conf_write(fp, " Dynamic interfaces = %s", data->dynamic_interfaces ? "true" : "false"); if (data->dynamic_interfaces) conf_write(fp, " Allow interface changes = %s", data->allow_if_changes ? "true" : "false"); if (data->no_email_faults) conf_write(fp, " Send emails for fault transitions = off"); #endif #ifdef _WITH_LVS_ if (data->lvs_timeouts.tcp_timeout) conf_write(fp, " LVS TCP timeout = %d", data->lvs_timeouts.tcp_timeout); if (data->lvs_timeouts.tcp_fin_timeout) conf_write(fp, " LVS TCP FIN timeout = %d", data->lvs_timeouts.tcp_fin_timeout); if (data->lvs_timeouts.udp_timeout) conf_write(fp, " LVS TCP timeout = %d", data->lvs_timeouts.udp_timeout); #ifdef _WITH_VRRP_ #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_VRRP) #endif conf_write(fp, " Default interface = %s", data->default_ifp ? data->default_ifp->ifname : DFLT_INT); conf_write(fp, " Disable local IGMP = %s", data->disable_local_igmp ? "yes" : "no"); conf_write(fp, " Use VRRPv2 checksum for VRRPv3 IPv4 = %s", data->v3_checksum_as_v2 ? "yes" : "no"); if (data->lvs_syncd.ifname) { if (data->lvs_syncd.vrrp) conf_write(fp, " LVS syncd vrrp instance = %s" , data->lvs_syncd.vrrp->iname); else if (data->lvs_syncd.vrrp_name) conf_write(fp, " LVS syncd vrrp name = %s" , data->lvs_syncd.vrrp_name); conf_write(fp, " LVS syncd interface = %s" , data->lvs_syncd.ifname); conf_write(fp, " LVS syncd syncid = %u" , data->lvs_syncd.syncid); #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ if (data->lvs_syncd.sync_maxlen) conf_write(fp, " LVS syncd maxlen = %u", data->lvs_syncd.sync_maxlen); if (data->lvs_syncd.mcast_group.ss_family != AF_UNSPEC) conf_write(fp, " LVS mcast group %s", inet_sockaddrtos(&data->lvs_syncd.mcast_group)); if (data->lvs_syncd.mcast_port) conf_write(fp, " LVS syncd mcast port = %d", data->lvs_syncd.mcast_port); if (data->lvs_syncd.mcast_ttl) conf_write(fp, " LVS syncd mcast ttl = %u", data->lvs_syncd.mcast_ttl); #endif } #endif conf_write(fp, " LVS flush = %s", data->lvs_flush ? "true" : "false"); conf_write(fp, " LVS flush on stop = %s", data->lvs_flush_on_stop == LVS_FLUSH_FULL ? "full" : data->lvs_flush_on_stop == LVS_FLUSH_VS ? "VS" : "disabled"); #endif if (data->notify_fifo.name) write_fifo_details(fp, &data->notify_fifo, "Global"); #ifdef _WITH_VRRP_ if (data->vrrp_notify_fifo.name) write_fifo_details(fp, &data->vrrp_notify_fifo, "VRRP"); #endif #ifdef _WITH_LVS_ if (data->lvs_notify_fifo.name) write_fifo_details(fp, &data->lvs_notify_fifo, "LVS"); #endif #ifdef _WITH_VRRP_ conf_write(fp, " FIFO write vrrp states on reload = %s", data->fifo_write_vrrp_states_on_reload ? "true" : "false"); conf_write(fp, " VRRP notify priority changes = %s", data->vrrp_notify_priority_changes ? "true" : "false"); if (data->vrrp_mcast_group4.sin_family) { conf_write(fp, " VRRP IPv4 mcast group = %s" , inet_sockaddrtos(PTR_CAST(sockaddr_t, &data->vrrp_mcast_group4))); } if (data->vrrp_mcast_group6.sin6_family) { conf_write(fp, " VRRP IPv6 mcast group = %s" , inet_sockaddrtos(PTR_CAST(sockaddr_t, &data->vrrp_mcast_group6))); } conf_write(fp, " Gratuitous ARP delay = %u", data->vrrp_garp_delay/TIMER_HZ); conf_write(fp, " Gratuitous ARP repeat = %u", data->vrrp_garp_rep); conf_write(fp, " Gratuitous ARP refresh timer = %" PRI_tv_sec, data->vrrp_garp_refresh.tv_sec); conf_write(fp, " Gratuitous ARP refresh repeat = %u", data->vrrp_garp_refresh_rep); conf_write(fp, " Gratuitous ARP lower priority delay = %u", data->vrrp_garp_lower_prio_delay == PARAMETER_UNSET ? PARAMETER_UNSET : data->vrrp_garp_lower_prio_delay / TIMER_HZ); conf_write(fp, " Gratuitous ARP lower priority repeat = %u", data->vrrp_garp_lower_prio_rep); conf_write(fp, " Num adverts before down = %u", data->vrrp_down_timer_adverts); #ifdef _HAVE_VRRP_VMAC_ if (data->vrrp_vmac_garp_intvl != PARAMETER_UNSET) conf_write(fp, " Gratuitous ARP for each secondary %s = %us", data->vrrp_vmac_garp_all_if ? "i/f" : "VMAC", data->vrrp_vmac_garp_intvl); #endif conf_write(fp, " Send advert after receive lower priority advert = %s", data->vrrp_lower_prio_no_advert ? "false" : "true"); conf_write(fp, " Send advert after receive higher priority advert = %s", data->vrrp_higher_prio_send_advert ? "true" : "false"); conf_write(fp, " Gratuitous ARP interval = %f", data->vrrp_garp_interval / TIMER_HZ_DOUBLE); conf_write(fp, " Gratuitous NA interval = %f", data->vrrp_gna_interval / TIMER_HZ_DOUBLE); conf_write(fp, " VRRP default protocol version = %d", data->vrrp_version); #ifdef _WITH_IPTABLES_ if (data->vrrp_iptables_inchain) { conf_write(fp," Iptables input chain = %s", data->vrrp_iptables_inchain); if (data->vrrp_iptables_outchain) conf_write(fp," Iptables output chain = %s", data->vrrp_iptables_outchain); #ifdef _HAVE_LIBIPSET_ conf_write(fp, " Using ipsets = %s", data->using_ipsets ? "true" : "false"); if (data->using_ipsets) { if (data->vrrp_ipset_address) conf_write(fp," ipset IPv4 address set = %s", data->vrrp_ipset_address); if (data->vrrp_ipset_address6) conf_write(fp," ipset IPv6 address set = %s", data->vrrp_ipset_address6); if (data->vrrp_ipset_address_iface6) conf_write(fp," ipset IPv6 address,iface set = %s", data->vrrp_ipset_address_iface6); if (data->vrrp_ipset_igmp) conf_write(fp," ipset IGMP set = %s", data->vrrp_ipset_igmp); if (data->vrrp_ipset_mld) conf_write(fp," ipset MLD set = %s", data->vrrp_ipset_mld); #ifdef _HAVE_VRRP_VMAC_ if (data->vrrp_ipset_vmac_nd) conf_write(fp," ipset ND set = %s", data->vrrp_ipset_vmac_nd); #endif } #endif } #endif #ifdef _WITH_NFTABLES_ #ifdef _WITH_VRRP_ if (data->vrrp_nf_table_name) { conf_write(fp," nftables table name = %s", data->vrrp_nf_table_name); conf_write(fp," nftables base chain priority = %d", data->vrrp_nf_chain_priority); conf_write(fp," nftables %sforce use ifindex for link local IPv6", data->vrrp_nf_ifindex ? "" : "don't "); } #endif #ifdef _WITH_LVS_ if (data->ipvs_nf_table_name) { conf_write(fp," ipvs nftables table name = %s", data->ipvs_nf_table_name); conf_write(fp," ipvs nftables base chain priority = %d", data->ipvs_nf_chain_priority); conf_write(fp," ipvs nftables start fwmark = %u", data->ipvs_nftables_start_fwmark); } #endif conf_write(fp," nftables with%s counters", data->nf_counters ? "" : "out"); conf_write(fp," libnftnl version %u.%u.%u", LIBNFTNL_VERSION >> 16, (LIBNFTNL_VERSION >> 8) & 0xff, LIBNFTNL_VERSION & 0xff); #endif conf_write(fp, " VRRP check unicast_src = %s", data->vrrp_check_unicast_src ? "true" : "false"); conf_write(fp, " VRRP skip check advert addresses = %s", data->vrrp_skip_check_adv_addr ? "true" : "false"); conf_write(fp, " VRRP strict mode = %s", data->vrrp_strict ? "true" : "false"); if (data->max_auto_priority == -1) conf_write(fp, " Max auto priority = Disabled"); else conf_write(fp, " Max auto priority = %d", data->max_auto_priority); conf_write(fp, " Min auto priority delay = %u usecs", data->min_auto_priority_delay); conf_write(fp, " VRRP process priority = %d", data->vrrp_process_priority); conf_write(fp, " VRRP don't swap = %s", data->vrrp_no_swap ? "true" : "false"); conf_write(fp, " VRRP realtime priority = %u", data->vrrp_realtime_priority); if (CPU_COUNT(&data->vrrp_cpu_mask)) { get_process_cpu_affinity_string(&data->vrrp_cpu_mask, cpu_str, 63); conf_write(fp, " VRRP CPU Affinity = %s", cpu_str); } conf_write(fp, " VRRP realtime limit = %" PRI_rlim_t, data->vrrp_rlimit_rt); #endif #ifdef _WITH_LVS_ conf_write(fp, " Checker process priority = %d", data->checker_process_priority); conf_write(fp, " Checker don't swap = %s", data->checker_no_swap ? "true" : "false"); conf_write(fp, " Checker realtime priority = %u", data->checker_realtime_priority); if (CPU_COUNT(&data->checker_cpu_mask)) { get_process_cpu_affinity_string(&data->checker_cpu_mask, cpu_str, 63); conf_write(fp, " Checker CPU Affinity = %s", cpu_str); } conf_write(fp, " Checker realtime limit = %" PRI_rlim_t, data->checker_rlimit_rt); #endif #ifdef _WITH_BFD_ conf_write(fp, " BFD process priority = %d", data->bfd_process_priority); conf_write(fp, " BFD don't swap = %s", data->bfd_no_swap ? "true" : "false"); conf_write(fp, " BFD realtime priority = %u", data->bfd_realtime_priority); if (CPU_COUNT(&data->bfd_cpu_mask)) { get_process_cpu_affinity_string(&data->bfd_cpu_mask, cpu_str, 63); conf_write(fp, " BFD CPU Affinity = %s", cpu_str); } conf_write(fp, " BFD realtime limit = %" PRI_rlim_t, data->bfd_rlimit_rt); #endif #ifdef _WITH_SNMP_VRRP_ conf_write(fp, " SNMP vrrp %s", data->enable_snmp_vrrp ? "enabled" : "disabled"); #endif #ifdef _WITH_SNMP_CHECKER_ conf_write(fp, " SNMP checker %s", data->enable_snmp_checker ? "enabled" : "disabled"); #endif #ifdef _WITH_SNMP_RFCV2_ conf_write(fp, " SNMP RFCv2 %s", data->enable_snmp_rfcv2 ? "enabled" : "disabled"); #endif #ifdef _WITH_SNMP_RFCV3_ conf_write(fp, " SNMP RFCv3 %s", data->enable_snmp_rfcv3 ? "enabled" : "disabled"); #endif #ifdef _WITH_SNMP_ conf_write(fp, " SNMP traps %s", data->enable_traps ? "enabled" : "disabled"); conf_write(fp, " SNMP socket = %s", data->snmp_socket ? data->snmp_socket : "default (unix:/var/agentx/master)"); #endif #ifdef _WITH_SNMP_CHECKER_ conf_write(fp, " SNMP VS stats update interval = %s", format_decimal(data->snmp_vs_stats_update_interval, TIMER_HZ_DIGITS)); conf_write(fp, " SNMP RS stats update interval = %s", format_decimal(data->snmp_rs_stats_update_interval, TIMER_HZ_DIGITS)); #endif #ifdef _WITH_DBUS_ conf_write(fp, " DBus %s", data->enable_dbus ? "enabled" : "disabled"); conf_write(fp, " DBus service name = %s", data->dbus_service_name ? data->dbus_service_name : ""); conf_write(fp, " DBus no interface name = %s", data->dbus_no_interface_name ? data->dbus_no_interface_name : dbus_no_interface_name); #endif conf_write(fp, " Script security %s", script_security ? "enabled" : "disabled"); if (!get_default_script_user(&uid, &gid)) conf_write(fp, " Default script uid:gid %u:%u", uid, gid); #ifdef _WITH_VRRP_ conf_write(fp, " vrrp_netlink_cmd_rcv_bufs = %u", global_data->vrrp_netlink_cmd_rcv_bufs); conf_write(fp, " vrrp_netlink_cmd_rcv_bufs_force = %d", global_data->vrrp_netlink_cmd_rcv_bufs_force); conf_write(fp, " vrrp_netlink_monitor_rcv_bufs = %u", global_data->vrrp_netlink_monitor_rcv_bufs); conf_write(fp, " vrrp_netlink_monitor_rcv_bufs_force = %d", global_data->vrrp_netlink_monitor_rcv_bufs_force); #ifdef _WITH_TRACK_PROCESS_ conf_write(fp, " process_monitor_rcv_bufs = %u", global_data->process_monitor_rcv_bufs); conf_write(fp, " process_monitor_rcv_bufs_force = %d", global_data->process_monitor_rcv_bufs_force); #endif #endif #ifdef _WITH_LVS_ conf_write(fp, " lvs_netlink_cmd_rcv_bufs = %u", global_data->lvs_netlink_cmd_rcv_bufs); conf_write(fp, " lvs_netlink_cmd_rcv_bufs_force = %d", global_data->lvs_netlink_cmd_rcv_bufs_force); conf_write(fp, " lvs_netlink_monitor_rcv_bufs = %u", global_data->lvs_netlink_monitor_rcv_bufs); conf_write(fp, " lvs_netlink_monitor_rcv_bufs_force = %d", global_data->lvs_netlink_monitor_rcv_bufs_force); conf_write(fp, " rs_init_notifies = %d", global_data->rs_init_notifies); conf_write(fp, " no_checker_emails = %d", global_data->no_checker_emails); #endif #ifdef _WITH_VRRP_ buf[0] = '\0'; if (global_data->vrrp_rx_bufs_policy & RX_BUFS_POLICY_MTU) strcpy(buf, " rx_bufs_policy = MTU"); else if (global_data->vrrp_rx_bufs_policy & RX_BUFS_POLICY_ADVERT) strcpy(buf, " rx_bufs_policy = ADVERT"); else if (global_data->vrrp_rx_bufs_policy & RX_BUFS_SIZE) sprintf(buf, " rx_bufs_size = %zu", global_data->vrrp_rx_bufs_size); if (buf[0]) conf_write(fp, "%s", buf); conf_write(fp, " rx_bufs_multiples = %d", global_data->vrrp_rx_bufs_multiples); conf_write(fp, " umask = 0%o", umask_val); if (global_data->vrrp_startup_delay) conf_write(fp, " vrrp_startup_delay = %g", global_data->vrrp_startup_delay / TIMER_HZ_DOUBLE); if (global_data->log_unknown_vrids) conf_write(fp, " log_unknown_vrids"); if (global_data->vrrp_owner_ignore_adverts) conf_write(fp, " vrrp_owner_ignore_adverts"); #ifdef _HAVE_VRRP_VMAC_ if (global_data->vmac_prefix) conf_write(fp, " VMAC prefix = %s", global_data->vmac_prefix); if (global_data->vmac_addr_prefix) conf_write(fp, " VMAC address prefix = %s", global_data->vmac_addr_prefix); #endif #endif if ((val = get_cur_priority())) conf_write(fp, " current realtime priority = %u", val); if ((val = get_cur_rlimit_rttime())) conf_write(fp, " current realtime time limit = %u", val); #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 if (global_data->state_dump_file) conf_write(fp, " state dump file %s", global_data->state_dump_file); if (global_data->stats_dump_file) conf_write(fp, " stats dump file %s", global_data->stats_dump_file); if (global_data->json_dump_file) conf_write(fp, " json dump file %s", global_data->json_dump_file); } keepalived-2.3.3/keepalived/core/sanitizer.c0000664000175000017500000000543514645450427014552 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Compiler sanitizer debugging * * Author: Alexandre Cassen, * * 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) 2024-2024 Alexandre Cassen, */ #include "config.h" #include #ifdef ASAN_LOG #include #include #endif #include #include #include "sanitizer.h" #include "logger.h" static const char *sanitizer_log = "/tmp/keepalived-sanitizer"; #ifdef _ASAN_DEFAULT_OPTIONS_ const char * __asan_default_options(void) { return _ASAN_DEFAULT_OPTIONS_; } #endif #ifdef _HWASAN_DEFAULT_OPTIONS_ const char * __hwasan_default_options(void) { return _HWASAN_DEFAULT_OPTIONS_; } #endif #ifdef _LSAN_DEFAULT_OPTIONS_ const char * __lsan_default_options(void) { return _LSAN_DEFAULT_OPTIONS_; } #endif #ifdef _MSAN_DEFAULT_OPTIONS_ const char * __msan_default_options(void) { return _MSAN_DEFAULT_OPTIONS_; } #endif #ifdef _SCUDO_DEFAULT_OPTIONS_ const char * __scudo_default_options(void) { return _SCUDO_DEFAULT_OPTIONS_; } #endif #ifdef _UBSAN_DEFAULT_OPTIONS_ const char * __ubsan_default_options(void) { return _UBSAN_DEFAULT_OPTIONS_; } #endif #ifdef ASAN_LOG /* Writing sanitizer output to the log is a good idea, but it is only implemented * for ASAN and not the other sanitizers. The default is therefore to be consistent * and write sanitizer output to a log file. */ static void err_call(const char *str) { const char *l_start, *l_end; for (l_start = str; l_start; l_start = l_end ? l_end + 1 : NULL) { l_end = strchr(l_start, '\n'); log_message(LOG_INFO, "%.*s", l_end ? (int)(l_end - l_start) : (int)strlen(l_start), l_start); } } #endif void __sanitizer_report_error_summary(const char *error_summary) { log_message(LOG_INFO, "sanitizer error %s: report written to %s.%d", error_summary, sanitizer_log, getpid()); } void sanitizer_init(void) { #ifdef ASAN_LOG __asan_set_error_report_callback(err_call); #endif __sanitizer_set_report_path(sanitizer_log); } keepalived-2.3.3/keepalived/core/snmp.c0000664000175000017500000003505114613547250013510 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: SNMP framework * * Authors: Vincent Bernat * * 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, */ #include "config.h" #include #include "scheduler.h" #include "snmp.h" #include "logger.h" #include "global_data.h" #include "main.h" #include "utils.h" #include "list_head.h" #include "warnings.h" #include static int snmp_keepalived_log(__attribute__((unused)) int major, __attribute__((unused)) int minor, void *serverarg, __attribute__((unused)) void *clientarg) { struct snmp_log_message *slm = PTR_CAST(struct snmp_log_message, serverarg); int slm_len = strlen(slm->msg); if (slm_len && slm->msg[slm_len-1] == '\n') slm_len--; log_message(slm->priority, "%.*s", slm_len, slm->msg); return 0; } /* Convert linux scope to InetScopeType */ unsigned long snmp_scope(int scope) { switch (scope) { case 0: return 14; /* global */ case 255: return 0; /* nowhere */ case 254: return 1; /* host */ case 253: return 2; /* link */ case 200: return 5; /* site */ default: return 0; } return 0; } list_head_t * snmp_header_list_head_table(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method, list_head_t *l) { oid target, current = 0; list_head_t *e; if (header_simple_table(vp, name, length, exact, var_len, write_method, -1) != MATCH_SUCCEEDED) return NULL; /* header_simple_table sets *var_len = 0 on error. On success it sets *var_len = sizeof(long), and *write_method = NULL. If we reach here, the success values will have been written. */ if (list_empty(l)) { if (var_len) *var_len = 0; return NULL; } target = name[*length - 1]; list_for_each(e, l) { if (++current < target) /* No match found yet */ continue; if (current == target) /* Exact match */ return e; if (exact) { /* No exact match found */ if (var_len) *var_len = 0; return NULL; } /* current is the best match */ name[*length - 1] = current; return e; } /* There are insufficent entries in the list or no match * at the end then just return no match */ if (var_len) *var_len = 0; return NULL; } list_head_t * snmp_find_element(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method, list_head_t *l, size_t offset_outer, size_t offset_inner) { oid *target, current[2]; size_t target_len; list_head_t *e, *e1; list_head_t *l1; int result; *write_method = 0; *var_len = sizeof(long); if (list_empty(l)) return NULL; if (exact && *length != (size_t)vp->namelen + 2) return NULL; if ((result = snmp_oid_compare(name, *length, vp->name, vp->namelen)) < 0) { memcpy(name, vp->name, sizeof(oid) * vp->namelen); *length = vp->namelen; } /* We search the best match: equal if exact, the lower OID in * the set of the OID strictly superior to the target * otherwise. */ target = &name[vp->namelen]; /* Our target match */ target_len = *length - vp->namelen; current[0] = 0; list_for_each(e, l) { current[0]++; if (target_len) { if (current[0] < target[0]) continue; /* Optimization: cannot be part of our set */ if (exact && current[0] > target[0]) return NULL; } /* Find the list head of the inner list in the outer entry */ l1 = PTR_CAST(list_head_t, ((char *)e - offset_outer + offset_inner)); current[1] = 0; list_for_each(e1, l1) { current[1]++; /* Compare to our target match */ if (target_len) { if ((result = snmp_oid_compare(current, 2, target, target_len)) < 0) continue; if (result == 0) { if (!exact) continue; /* Got an exact match and asked for it */ return e1; } if (exact) { /* result > 0, so no match */ return NULL; } } /* This is our best match */ memcpy(target, current, sizeof(oid) * 2); *length = (unsigned)vp->namelen + 2; return e1; } } /* No match at all */ return NULL; } enum snmp_global_magic { SNMP_KEEPALIVEDVERSION, SNMP_ROUTERID, SNMP_MAIL_SMTPSERVERADDRESSTYPE, SNMP_MAIL_SMTPSERVERADDRESS, SNMP_MAIL_SMTPSERVERTIMEOUT, SNMP_MAIL_EMAILFROM, SNMP_MAIL_EMAILADDRESS, SNMP_MAIL_EMAILFAULTS, SNMP_MAIL_SMTPSERVERPORT, SNMP_TRAPS, SNMP_LINKBEAT, SNMP_LVSFLUSH, SNMP_LVSFLUSH_ONSTOP, SNMP_V3_CHECKSUM_AS_V2, SNMP_IPVS_64BIT_STATS, SNMP_NET_NAMESPACE, SNMP_DBUS, SNMP_DYNAMIC_INTERFACES, SNMP_SMTP_ALERT, SNMP_SMTP_ALERT_VRRP, SNMP_SMTP_ALERT_CHECKER, }; static u_char* snmp_scalar(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { static unsigned long long_ret; snmp_ret_t ret; if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; switch (vp->magic) { case SNMP_KEEPALIVEDVERSION: *var_len = strlen(version_string); ret.cp = version_string; return ret.p; case SNMP_ROUTERID: if (!global_data->router_id) return NULL; *var_len = strlen(global_data->router_id); ret.cp = global_data->router_id; return ret.p; case SNMP_MAIL_SMTPSERVERADDRESSTYPE: long_ret = SNMP_InetAddressType(global_data->smtp_server.ss_family); return PTR_CAST(u_char, &long_ret); case SNMP_MAIL_SMTPSERVERADDRESS: if (global_data->smtp_server.ss_family == AF_INET6) { struct sockaddr_in6 *addr6 = PTR_CAST(struct sockaddr_in6, &global_data->smtp_server); *var_len = 16; return PTR_CAST(u_char, &addr6->sin6_addr); } else { struct sockaddr_in *addr4 = PTR_CAST(struct sockaddr_in, &global_data->smtp_server); *var_len = 4; return PTR_CAST(u_char, &addr4->sin_addr); } return NULL; case SNMP_MAIL_SMTPSERVERPORT: long_ret = ntohs(inet_sockaddrport(&global_data->smtp_server)); return PTR_CAST(u_char, &long_ret); case SNMP_MAIL_SMTPSERVERTIMEOUT: long_ret = global_data->smtp_connection_to / TIMER_HZ; return PTR_CAST(u_char, &long_ret); case SNMP_MAIL_EMAILFROM: if (!global_data->email_from) return NULL; *var_len = strlen(global_data->email_from); ret.cp = global_data->email_from; return ret.p; #ifdef _WITH_VRRP_ case SNMP_MAIL_EMAILFAULTS: long_ret = SNMP_TruthValue(!global_data->no_email_faults); return PTR_CAST(u_char, &long_ret); #endif case SNMP_TRAPS: long_ret = SNMP_TruthValue(global_data->enable_traps); return PTR_CAST(u_char, &long_ret); #ifdef _WITH_LINKBEAT_ case SNMP_LINKBEAT: long_ret = global_data->linkbeat_use_polling ? 2 : 1; return PTR_CAST(u_char, &long_ret); #endif #ifdef _WITH_LVS_ case SNMP_LVSFLUSH: long_ret = SNMP_TruthValue(global_data->lvs_flush); return PTR_CAST(u_char, &long_ret); case SNMP_LVSFLUSH_ONSTOP: long_ret = global_data->lvs_flush_on_stop == LVS_FLUSH_FULL ? 1 : global_data->lvs_flush_on_stop == LVS_FLUSH_VS ? 3 : 2; return PTR_CAST(u_char, &long_ret); #endif #ifdef _WITH_VRRP_ case SNMP_V3_CHECKSUM_AS_V2: long_ret = SNMP_TruthValue(global_data->v3_checksum_as_v2); return PTR_CAST(u_char, &long_ret); #endif case SNMP_IPVS_64BIT_STATS: #ifdef _WITH_LVS_64BIT_STATS_ long_ret = 1; #else long_ret = 2; #endif return PTR_CAST(u_char, &long_ret); case SNMP_NET_NAMESPACE: if (global_data->network_namespace) { *var_len = strlen(global_data->network_namespace); ret.cp = global_data->network_namespace; return ret.p; } *var_len = 0; ret.cp = ""; return ret.p; case SNMP_DBUS: #ifdef _WITH_DBUS_ if (global_data->enable_dbus) long_ret = 1; else #endif long_ret = 2; return PTR_CAST(u_char, &long_ret); #ifdef _WITH_VRRP_ case SNMP_DYNAMIC_INTERFACES: long_ret = SNMP_TruthValue(global_data->dynamic_interfaces); return PTR_CAST(u_char, &long_ret); #endif case SNMP_SMTP_ALERT: long_ret = global_data->smtp_alert == -1 ? 3 : global_data->smtp_alert ? 1 : 2; return PTR_CAST(u_char, &long_ret); #ifdef _WITH_VRRP_ case SNMP_SMTP_ALERT_VRRP: long_ret = global_data->smtp_alert_vrrp == -1 ? 3 : global_data->smtp_alert_vrrp ? 1 : 2; return PTR_CAST(u_char, &long_ret); #endif #ifdef _WITH_LVS_ case SNMP_SMTP_ALERT_CHECKER: long_ret = global_data->smtp_alert_checker == -1 ? 3 : global_data->smtp_alert_checker ? 1 : 2; return PTR_CAST(u_char, &long_ret); #endif default: break; } return NULL; } static u_char * snmp_mail(struct variable *vp, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { email_t *email; list_head_t *e; struct { /* We need to cast aware const */ u_char *uc; const u_char *cuc; } ret; if ((e = snmp_header_list_head_table(vp, name, length, exact, var_len, write_method, &global_data->email)) == NULL) return NULL; email = list_entry(e, email_t, e_list); switch (vp->magic) { case SNMP_MAIL_EMAILADDRESS: *var_len = strlen(email->addr); ret.cuc = PTR_CAST_CONST(u_char, email->addr); return ret.uc; default: break; } return NULL; } static const char global_name[] = "Keepalived"; static oid global_oid[] = GLOBAL_OID; static struct variable4 global_vars[] = { /* version */ {SNMP_KEEPALIVEDVERSION, ASN_OCTET_STR, RONLY, snmp_scalar, 1, {1}}, /* routerId */ {SNMP_ROUTERID, ASN_OCTET_STR, RONLY, snmp_scalar, 1, {2}}, /* mail */ {SNMP_MAIL_SMTPSERVERADDRESSTYPE, ASN_INTEGER, RONLY, snmp_scalar, 2, {3, 1}}, {SNMP_MAIL_SMTPSERVERADDRESS, ASN_OCTET_STR, RONLY, snmp_scalar, 2, {3, 2}}, {SNMP_MAIL_SMTPSERVERTIMEOUT, ASN_UNSIGNED, RONLY, snmp_scalar, 2, {3, 3}}, {SNMP_MAIL_EMAILFROM, ASN_OCTET_STR, RONLY, snmp_scalar, 2, {3, 4}}, /* emailTable */ {SNMP_MAIL_EMAILADDRESS, ASN_OCTET_STR, RONLY, snmp_mail, 4, {3, 5, 1, 2}}, /* SMTP server port */ {SNMP_MAIL_SMTPSERVERPORT, ASN_UNSIGNED, RONLY, snmp_scalar, 2, {3, 6}}, /* are vrrp fault state transitions emailed */ {SNMP_MAIL_EMAILFAULTS, ASN_INTEGER, RONLY, snmp_scalar, 2, {3, 7}}, {SNMP_SMTP_ALERT, ASN_INTEGER, RONLY, snmp_scalar, 2, {3, 8}}, #ifdef _WITH_VRRP_ {SNMP_SMTP_ALERT_VRRP, ASN_INTEGER, RONLY, snmp_scalar, 2, {3, 9}}, #endif #ifdef _WITH_LVS_ {SNMP_SMTP_ALERT_CHECKER, ASN_INTEGER, RONLY, snmp_scalar, 2, {3, 10}}, #endif /* trapEnable */ {SNMP_TRAPS, ASN_INTEGER, RONLY, snmp_scalar, 1, {4}}, /* linkBeat */ {SNMP_LINKBEAT, ASN_INTEGER, RONLY, snmp_scalar, 1, {5}}, #ifdef _WITH_LVS_ /* lvsFlush */ {SNMP_LVSFLUSH, ASN_INTEGER, RONLY, snmp_scalar, 1, {6}}, #endif #ifdef _WITH_LVS_64BIT_STATS_ /* LVS 64-bit stats */ {SNMP_IPVS_64BIT_STATS, ASN_INTEGER, RONLY, snmp_scalar, 1, {7}}, #endif {SNMP_NET_NAMESPACE, ASN_OCTET_STR, RONLY, snmp_scalar, 1, {8}}, #ifdef _WITH_DBUS_ {SNMP_DBUS, ASN_INTEGER, RONLY, snmp_scalar, 1, {9}}, #endif #ifdef _WITH_VRRP_ {SNMP_DYNAMIC_INTERFACES, ASN_INTEGER, RONLY, snmp_scalar, 1, {10}}, #endif #ifdef _WITH_LVS_ /* lvsFlushOnStop */ {SNMP_LVSFLUSH_ONSTOP, ASN_INTEGER, RONLY, snmp_scalar, 1, {11}}, #endif #ifdef _WITH_VRRP_ {SNMP_V3_CHECKSUM_AS_V2, ASN_INTEGER, RONLY, snmp_scalar, 1, {12}}, #endif }; static int snmp_setup_session_cb(__attribute__((unused)) int majorID, __attribute__((unused)) int minorID, void *serverarg, __attribute__((unused)) void *clientarg) { netsnmp_session *sess = serverarg; if (serverarg == NULL) return 0; /* * Because ping are done synchronously, we do everything to * avoid to block too long. Better disconnect from the master * agent than waiting for him... */ sess->timeout = ONE_SEC / 3; sess->retries = 0; return 0; } void snmp_register_mib(oid *myoid, size_t len, const char *name, struct variable *variables, size_t varsize, size_t varlen) { char name_buf[80]; if (register_mib(name, PTR_CAST(struct variable, variables), varsize, varlen, myoid, len) != MIB_REGISTERED_OK) log_message(LOG_WARNING, "Unable to register %s MIB", name); snprintf(name_buf, sizeof(name_buf), "The MIB module for %s", name); register_sysORTable(myoid, len, name_buf); } void snmp_unregister_mib(oid *myoid, size_t len) { unregister_sysORTable(myoid, len); } void snmp_agent_init(const char *snmp_socket_name, bool base_mib) { if (snmp_running) return; log_message(LOG_INFO, "Starting SNMP subagent"); netsnmp_enable_subagent(); snmp_disable_log(); snmp_enable_calllog(); snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_LOGGING, snmp_keepalived_log, NULL); /* Do not handle persistent states */ netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, TRUE); /* Do not load any MIB */ setenv("MIBS", "", 1); /* * We also register a callback to modify default timeout and * retries value. */ snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SESSION_INIT, snmp_setup_session_cb, NULL); /* Specify the socket to master agent, if provided */ if (snmp_socket_name != NULL) { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, snmp_socket_name); } /* * Ping AgentX less often than every 15 seconds: pinging can * block keepalived. We check every 2 minutes. */ netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_PING_INTERVAL, 120); /* Tell library not to raise SIGALRM */ netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_ALARM_DONT_USE_SIG, 1); init_agent(global_name); if (base_mib) snmp_register_mib(global_oid, OID_LENGTH(global_oid), global_name, PTR_CAST(struct variable, global_vars), sizeof(global_vars[0]), sizeof(global_vars)/sizeof(global_vars[0])); init_snmp(global_name); master->snmp_timer_thread = thread_add_timer(master, snmp_timeout_thread, 0, TIMER_NEVER); /* Set up the fd threads */ snmp_epoll_info(master); snmp_running = true; } void snmp_agent_close(bool base_mib) { if (!snmp_running) return; snmp_epoll_clear(master); if (base_mib) snmp_unregister_mib(global_oid, OID_LENGTH(global_oid)); snmp_shutdown(global_name); shutdown_agent(); snmp_running = false; } #ifdef THREAD_DUMP void register_snmp_addresses(void) { register_thread_address("snmp_timeout_thread", snmp_timeout_thread); } #endif keepalived-2.3.3/keepalived/core/libnl_link.c0000664000175000017500000001232113663215160014637 /* * libnl_link: Handle dynamic linking to netlink libraries * * Authors: P. Quentin Armitage * * 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) 2017-2017 Alexandre Cassen, */ #include "config.h" #ifdef _LIBNL_DYNAMIC_ #include #include #include #include #ifdef LIBIPVS_USE_NL #include #include #endif #include "libnl_link.h" #include "logger.h" /* The addresses of the functions we want */ struct nl_sock * (*nl_socket_alloc_addr)(void); void (*nl_socket_free_addr)(struct nl_sock *); #ifdef LIBIPVS_USE_NL int (*genl_connect_addr)(struct nl_sock *); int (*genl_ctrl_resolve_addr)(struct nl_sock *, const char *); int (*genlmsg_parse_addr)(struct nlmsghdr *, int, struct nlattr **, int, struct nla_policy *); void * (*genlmsg_put_addr)(struct nl_msg *, uint32_t, uint32_t, int, int, int, uint8_t, uint8_t); int (*nla_nest_end_addr)(struct nl_msg *, struct nlattr *); struct nlattr * (*nla_nest_start_addr)(struct nl_msg *, int); int (*nla_put_daddr)(struct nl_msg *, int, int, const void *); struct nl_msg * (*nlmsg_alloc_addr)(void); void (*nlmsg_free_addr)(struct nl_msg *); struct nlmsghdr * (*nlmsg_hdr_addr)(struct nl_msg *); int (*nl_recvmsgs_default_addr)(struct nl_sock *); int (*nl_send_auto_addr)(struct nl_sock *, struct nl_msg *); int (*nl_socket_modify_cb_addr)(struct nl_sock *, enum nl_cb_type, enum nl_cb_kind, nl_recvmsg_msg_cb_t, void *); int (*nl_socket_modify_err_cb_addr)(struct nl_sock *, enum nl_cb_kind, nl_recvmsg_err_cb_t, void *); #ifdef _HAVE_LIBNL3_ void * (*nla_data_addr)(const struct nlattr *); int32_t (*nla_get_s32_addr)(const struct nlattr *); char * (*nla_get_string_addr)(const struct nlattr *); uint16_t (*nla_get_u16_addr)(const struct nlattr *); uint32_t (*nla_get_u32_addr)(const struct nlattr *); uint64_t (*nla_get_u64_addr)(const struct nlattr *); int (*nla_memcpy_addr)(void *, const struct nlattr *, int); int (*nla_parse_nested_addr)(struct nlattr **, int, struct nlattr *, struct nla_policy *); #endif #endif static void* libnl_handle; #ifdef LIBIPVS_USE_NL static void* libnl_genl_handle; #endif bool libnl_init(void) { if (libnl_handle) return true; /* Attempt to open the necessary libraries */ #ifdef _HAVE_LIBNL1_ #ifdef _WITH_LVS_ if (!(libnl_handle = dlopen("libnl.so", RTLD_NOW)) && !(libnl_handle = dlopen(NL_LIB_NAME, RTLD_NOW))) { log_message(LOG_INFO, "Unable to load nl library - %s", dlerror()); return false; } libnl_genl_handle = libnl_handle; #endif #else if (!(libnl_handle = dlopen("libnl-3.so", RTLD_NOW)) && !(libnl_handle = dlopen(NL3_LIB_NAME, RTLD_NOW))) { log_message(LOG_INFO, "Unable to load nl-3 library - %s", dlerror()); return false; } #ifdef _WITH_LVS_ if (!(libnl_genl_handle = dlopen("libnl-genl-3.so", RTLD_NOW)) && !(libnl_genl_handle = dlopen(NL3_GENL_LIB_NAME, RTLD_NOW))) { log_message(LOG_INFO, "Unable to load nl-genl-3 library - %s", dlerror()); return false; } #endif #endif if ( #ifdef _HAVE_LIBNL1_ !(nl_socket_alloc_addr = dlsym(libnl_handle, "nl_handle_alloc")) || !(nl_socket_free_addr = dlsym(libnl_handle, "nl_handle_destroy")) || #else !(nl_socket_alloc_addr = dlsym(libnl_handle, "nl_socket_alloc")) || !(nl_socket_free_addr = dlsym(libnl_handle, "nl_socket_free")) || #endif #ifdef _WITH_LVS_ !(genl_connect_addr = dlsym(libnl_genl_handle, "genl_connect")) || !(genl_ctrl_resolve_addr = dlsym(libnl_genl_handle, "genl_ctrl_resolve")) || !(genlmsg_parse_addr = dlsym(libnl_genl_handle, "genlmsg_parse")) || !(genlmsg_put_addr = dlsym(libnl_genl_handle, "genlmsg_put")) || !(nla_nest_end_addr = dlsym(libnl_handle, "nla_nest_end")) || !(nla_nest_start_addr = dlsym(libnl_handle, "nla_nest_start")) || !(nla_put_daddr = dlsym(libnl_handle, "nla_put")) || !(nlmsg_alloc_addr = dlsym(libnl_handle, "nlmsg_alloc")) || !(nlmsg_free_addr = dlsym(libnl_handle, "nlmsg_free")) || !(nlmsg_hdr_addr = dlsym(libnl_handle, "nlmsg_hdr")) || !(nl_recvmsgs_default_addr = dlsym(libnl_handle, "nl_recvmsgs_default")) || !(nl_send_auto_addr = dlsym(libnl_handle, "nl_send_auto")) || !(nl_socket_modify_cb_addr = dlsym(libnl_handle, "nl_socket_modify_cb")) || !(nl_socket_modify_err_cb_addr = dlsym(libnl_handle, "nl_socket_modify_err_cb")) || #ifdef _HAVE_LIBNL3_ !(nla_data_addr = dlsym(libnl_handle, "nla_data")) || !(nla_get_s32_addr = dlsym(libnl_handle, "nla_get_s32")) || !(nla_get_string_addr = dlsym(libnl_handle, "nla_get_string")) || !(nla_get_u16_addr = dlsym(libnl_handle, "nla_get_u16")) || !(nla_get_u32_addr = dlsym(libnl_handle, "nla_get_u32")) || !(nla_get_u64_addr = dlsym(libnl_handle, "nla_get_u64")) || !(nla_memcpy_addr = dlsym(libnl_handle, "nla_memcpy")) || !(nla_parse_nested_addr = dlsym(libnl_handle, "nla_parse_nested")) || #endif #endif false) log_message(LOG_INFO, "Failed to dynamic link a libnli/libnl-3 function"); return true; } #endif keepalived-2.3.3/keepalived/core/namespaces.c0000664000175000017500000003255114131250231014635 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Linux namespace handling. * * Author: Quentin Armitage * * 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) 2016-2017 Alexandre Cassen, */ /******************************************************************************* * * Running keepalived in a namespace provides isolation from other instances of * keepalived running on the same system, and is useful for a variety of reasons. * * In order not to have to specify different pid files for each instance of * keepalived, if keepalived is running in a network namespace it will also create * its own mount namespace, and will slave bind mount a unique directory * (/run/keepalived/NAMESPACE) on /run/keepalived, so keepalived will * write its usual pid files (but to /run/keepalived rather than to /run), * and outside the mount namespace these will be visible at * /run/keepalived/NAMESPACE. * * If you are familiar with network namespaces, then you will know what you can do * with them. If not, then the following scenarios should give you an idea of what * can be done, and why they might be helpful. * * If you wish to test keepalived, but don't wish to interfere with the * networking on a live system, or you wish to test multiple instances, but all * in one machine, then the following will set up such an environment. * * Three "machine" configuration: * * netns1 netns2 netns3 * ------------------ ------------------ ------------------ * | | | ----------- | | | * | | | | br0 | | | | * | | | ----------- | | | * | | | | | | | | * | e | | e e | | e | * | t | | t t | | t | * | h | | h h | | h | * | 0 | | 0 1 | | 0 | * | | | | | | | | | | * ------------------ ------------------ ------------------ * | | | | * --------------------------- --------------------------- * * NOTE: it is possible that `ip netns add NAME` will create the namespace but * include all the network links, rather than just lo. To check this, after * creating a namespace, run `ip netns exec NAME ip link show` and if it shows * all the network links, then you have this problem. To work around the problem, * prefix all the `ip netns add NAME` commands with unshare --net, e.g. * unshare --net ip netns add NAME * * Create the namespaces * # ip netns add netns1 * # ip netns add netns2 * # ip netns add netns3 * * Bring up the loopback interfaces * # ip netns exec netns1 ip link set lo up * # ip netns exec netns2 ip link set lo up * # ip netns exec netns3 ip link set lo up * * Create link between netns1 and netns2 * # ip netns exec netns2 ip link add 1.eth0 type veth peer name eth0 * # ip netns exec netns2 ip link set eth0 netns netns1 * * Create link between netns2 and netns3 * # ip netns exec netns2 ip link add 3.eth1 type veth peer name eth0 * # ip netns exec netns2 ip link set eth0 netns netns3 * * Make the link names in netns2 easier to remember * # ip netns exec netns2 ip link set 1.eth0 name eth0 * # ip netns exec netns2 ip link set 3.eth1 name eth1 * * Bring up the interfaces * # ip netns exec netns1 ip link set eth0 up * # ip netns exec netns2 ip link set eth0 up * # ip netns exec netns2 ip link set eth1 up * # ip netns exec netns3 ip link set eth0 up * * Bridge eth0 and eth1 in netns2 * # ip netns exec netns2 ip link add br0 type bridge * # ip netns exec netns2 ip link set br0 up * * Connect eth0 and eth1 to br0 in netns2 * # ip netns exec netns2 ip link set eth0 master br0 * # ip netns exec netns2 ip link set eth1 master br0 * * Configure some addresses * # ip netns exec netns1 ip addr add 10.2.0.1/24 broadcast 10.2.0.255 dev eth0 * # ip netns exec netns2 ip addr add 10.2.0.2/24 broadcast 10.2.0.255 dev br0 * # ip netns exec netns3 ip addr add 10.2.0.3/24 broadcast 10.2.0.255 dev eth0 * * Test it * # ip netns exec netns1 ping 10.2.0.2 # netns1 can talk to netns2 * # ip netns exec netns1 ping 10.2.0.3 # netns1 can talk to netns3 (bridge is working) * * If you want to enter multiple commands in a net namespace, then try: * # ip netns exec netns1 bash * # PS1="netns1 # " * netns1 # * * Create three configuration files, keepalived.netns1.conf etc * and in each config file in the global_defs section specify * net_namespace netns1 # or netns2 or netns3 as appropriate * global_defs { * .... * * Now run three instances of keepalived. Note, keepalived handles * joining the appropriate network namespace, and so the commands don't * need to be prefixed with 'ip netns exec netns1'. * # keepalived -f /etc/keepalived/keepalived.netns1.conf * # keepalived -f /etc/keepalived/keepalived.netns2.conf * # keepalived -f /etc/keepalived/keepalived.netns3.conf * * The syslog output will have the network namespace name appended to the * ident. * * If you want to connect the setup above to the real world, add the following: * # ip link add veth0 type veth peer name veth1 * # ip link set veth1 netns netns2 * # ip link set up veth0 * # ip link set veth1 netns netns2 * # ip netns exec netns2 ip link set up veth1 * # ip netns exec netns2 ip link set veth1 master br0 * # ip link add br0 type bridge * # ip link set br0 up * # ip link set veth0 master br0 * # ip link set eth0 master br0 * # ip link add addr 10.2.0.4/24 broadcast 10.2.0.255 dev br0 * * There are further possibilities. If the above configuration is set up on two * separate machines, a tunnel could be established between the two netns2 instances * and the masters of each end of the tunnels set to br0. Alternatively, a new vlan * could be set up in (or moved to) the two netns2 instances, and added to the br0 * bridges. * ******************************************************************************/ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LIBIPVS_USE_NL #ifdef _HAVE_LIBNL1_ #include #endif #include /* Required for netlink/headers.h prior to libnl v3.3.0 */ #include #include #ifdef _LIBNL_DYNAMIC_ #include "libnl_link.h" #endif #endif #include "namespaces.h" #include "memory.h" #include "logger.h" #include "pidfile.h" #include "utils.h" #include "bitops.h" /* Local data */ static const char *netns_dir = RUNSTATEDIR "/netns/"; static char *mount_dirname; static bool run_mount_set; void free_dirname(void) { FREE_PTR(mount_dirname); mount_dirname = NULL; } static void set_run_mount(const char *net_namespace) { bool error; /* /run/keepalived/NAMESPACE */ mount_dirname = MALLOC(strlen(KEEPALIVED_PID_DIR) + 1 + strlen(net_namespace)); if (!mount_dirname) { log_message(LOG_INFO, "Unable to allocate memory for pid file dirname"); return; } strcpy(mount_dirname, KEEPALIVED_PID_DIR); strcat(mount_dirname, net_namespace); /* We want the directory to have rwxr-xr-x permissions */ if (umask_val & (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) umask(umask_val & ~(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); error = mkdir(mount_dirname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && errno != EEXIST; /* Restore our default umask */ if (umask_val & (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) umask(umask_val); if (error) { log_message(LOG_INFO, "Unable to create directory %s", mount_dirname); free_dirname(); return; } if (unshare(CLONE_NEWNS)) { log_message(LOG_INFO, "mount unshare failed (%d) '%s'", errno, strerror(errno)); return; } /* Make all mounts unshared - systemd makes them shared by default */ if (mount("", "/", NULL, MS_REC | MS_SLAVE, NULL)) log_message(LOG_INFO, "Mount slave failed, error (%d) '%s'", errno, strerror(errno)); if (mount(mount_dirname, pid_directory, NULL, MS_BIND, NULL)) log_message(LOG_INFO, "Mount failed, error (%d) '%s'", errno, strerror(errno)); run_mount_set = true; } static void unmount_run(void) { if (!run_mount_set) return; if (umount(pid_directory)) log_message(LOG_INFO, "unmount of %s failed - errno %d", pid_directory, errno); if (mount_dirname) { if (rmdir(mount_dirname) && errno != ENOTEMPTY && errno != EBUSY) log_message(LOG_INFO, "unlink of %s failed - error (%d) '%s'", mount_dirname, errno, strerror(errno)); free_dirname(); } } bool set_namespaces(const char* net_namespace) { char *netns_path; int fd; netns_path = MALLOC(strlen(netns_dir) + strlen(net_namespace) + 1); if (!netns_path) { log_message(LOG_INFO, "Unable to malloc for set_namespaces()"); return false; } strcpy(netns_path, netns_dir); strcat(netns_path, net_namespace); fd = open(netns_path, O_RDONLY); if (fd == -1) { log_message(LOG_INFO, "Failed to open %s", netns_path); goto err; } if (setns(fd, CLONE_NEWNET)) { log_message(LOG_INFO, "setns() failed with error %d", errno); goto err; } close(fd); if (!__test_bit(CONFIG_TEST_BIT, &debug)) set_run_mount(net_namespace); FREE_PTR(netns_path); netns_path = NULL; return true; err: if (fd != -1) close(fd); FREE_PTR(netns_path); netns_path = NULL; return false; } void clear_namespaces(void) { unmount_run(); } /* get default namespace file descriptor to be able to place fd in a given * namespace */ static int open_current_namespace(void) { return open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC); } /* opens namespace for ipvs communication */ static int open_ipvs_namespace(const char *ns_name) { char netns_path[PATH_MAX]; const char *path; if (ns_name[0]) { if (snprintf(netns_path, sizeof(netns_path), "/var/run/netns/%s", ns_name) < 0) return -1; path = netns_path; } else path = "/proc/1/ns/net"; return open(path, O_RDONLY | O_CLOEXEC); } #ifdef _INCLUDE_UNUSED_CODE_ /* opens a socket in the namespace with the parameters , * and and returns the FD or -1 in case of error (check errno). */ int socket_netns(int nsfd, int domain, int type, int protocol) { int sock; if (default_namespace >= 0 && nsfd && setns(nsfd, CLONE_NEWNET) == -1) return -1; sock = socket(domain, type, protocol); if (default_namespace >= 0 && nsfd && setns(default_namespace, CLONE_NEWNET) == -1) { if (sock >= 0) close(sock); return -1; } return sock; } #endif int set_netns_name(const char *netns_name) { int cur_net_namespace = -1; int new_net_namespace; int ret; if (!netns_name) return -1; new_net_namespace = open_ipvs_namespace(netns_name); cur_net_namespace = open_current_namespace(); if (cur_net_namespace < 0 || new_net_namespace < 0) { if (cur_net_namespace >= 0) close(cur_net_namespace); else if (new_net_namespace >= 0) close(new_net_namespace); log_message(LOG_INFO, "Failed to open namespace fds, errno %d (%m)", errno); return -1; } ret = setns(new_net_namespace, CLONE_NEWNET); close(new_net_namespace); if (ret < 0) { close(cur_net_namespace); log_message(LOG_INFO, "Open socket - unable to set namespace, errno %d (%m)", errno); return -1; } return cur_net_namespace; } void restore_net_namespace(int cur_net_namespace) { int ret; ret = setns(cur_net_namespace, CLONE_NEWNET); close(cur_net_namespace); if (ret < 0) { log_message(LOG_INFO, "Open socket - unable to restore default namespace, errno %d (%m)", errno); return; } } int socket_netns_name(const char *netns_name, int domain, int type, int protocol) { int cur_net_namespace = -1; int sock; if (netns_name && (cur_net_namespace = set_netns_name(netns_name)) < 0) return -1; sock = socket(domain, type, protocol); if (cur_net_namespace >= 0) restore_net_namespace(cur_net_namespace); return sock; } #ifdef LIBIPVS_USE_NL /* netlink connect within ipvs namespace */ int nl_ipvs_connect(const char *netns_name, struct nl_sock *sock) { int cur_net_namespace = -1; int ret; if (netns_name && (cur_net_namespace = set_netns_name(netns_name)) < 0) return -1; ret = genl_connect(sock); if (cur_net_namespace >= 0) restore_net_namespace(cur_net_namespace); return ret < 0 ? -1 : 0; } #endif keepalived-2.3.3/keepalived/core/layer4.c0000664000175000017500000003037314700171421013723 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Layer4 checkers handling. Register worker threads & * upper layer checkers. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include #include #ifdef _WITH_LVS_ #include #include #ifdef ERRQUEUE_NEEDS_SYS_TIME #include #endif #include #endif #include "layer4.h" #include "logger.h" #include "scheduler.h" #ifdef _WITH_LVS_ #include "check_api.h" #endif #include "bitops.h" #include "utils.h" #include "align.h" // #define ICMP_DEBUG 1 #ifdef _WITH_LVS_ #define UDP_BUFSIZE 32 #endif #ifdef _WITH_LVS_ void set_buf(char *buf, size_t buf_len) { const char *str = "keepalived check - "; size_t str_len = strlen(str); char *p = buf; /* We need to overwrite the send buffer to avoid leaking * stack content. */ while (buf_len >= str_len) { memcpy(p, str, str_len); p += str_len; buf_len -= str_len; } if (buf_len) memcpy(p, str, buf_len); } #endif #ifndef _WITH_LVS_ static #endif enum connect_result socket_bind_connect(int fd, conn_opts_t *co) { int opt; socklen_t optlen; socklen_t addrlen; int ret; const sockaddr_t *addr = &co->dst; const sockaddr_t *bind_addr = &co->bindto; optlen = sizeof(opt); if (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void *)&opt, &optlen) < 0) { log_message(LOG_ERR, "Can't get socket type: %s", strerror(errno)); return connect_error; } #ifdef _WITH_SO_MARK_ if (co->fwmark) { if (setsockopt (fd, SOL_SOCKET, SO_MARK, &co->fwmark, sizeof (co->fwmark)) < 0) { log_message(LOG_ERR, "Error setting fwmark %u to socket: %s", co->fwmark, strerror(errno)); return connect_error; } } #endif if (co->bind_if[0]) { if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, co->bind_if, (unsigned)strlen(co->bind_if) + 1) < 0) { log_message(LOG_INFO, "Checker can't bind to device %s: %s", co->bind_if, strerror(errno)); return connect_error; } } /* Bind socket */ if (PTR_CAST_CONST(struct sockaddr, bind_addr)->sa_family != AF_UNSPEC) { addrlen = sizeof(*bind_addr); if (bind(fd, PTR_CAST_CONST(struct sockaddr, bind_addr), addrlen) != 0) { log_message(LOG_INFO, "Checker bind failed: %s", strerror(errno)); return connect_error; } } /* Set remote IP and connect */ addrlen = sizeof(*addr); ret = connect(fd, PTR_CAST_CONST(struct sockaddr, addr), addrlen); /* Immediate success */ if (ret == 0) return connect_success; /* If connect is in progress then return 1 else it's real error. */ if (errno == EINPROGRESS) return connect_in_progress; /* ENETUNREACH can be returned here. I'm not sure * about any of the others, but play safe. These * should all be considered to be a failure to connect * rather than a failure to run the check. */ if (errno == ENETUNREACH || errno == EHOSTUNREACH || errno == ECONNREFUSED || errno == EHOSTDOWN || errno == ENETDOWN || errno == ECONNRESET || errno == ECONNABORTED || errno == ETIMEDOUT) return connect_fail; /* We want to know about the error, but not repeatedly */ if (errno != co->last_errno) { co->last_errno = errno; if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "socket connect error %d - %m", errno); } return connect_error; } enum connect_result socket_connect(int fd, const sockaddr_t *addr) { conn_opts_t co = { .dst = *addr }; return socket_bind_connect(fd, &co); } enum connect_result socket_state(thread_ref_t thread, thread_func_t func, unsigned extra_flags) { int status; socklen_t addrlen; timeval_t timer_min; /* Handle connection timeout */ if (thread->type == THREAD_WRITE_TIMEOUT) { thread_close_fd(thread); return connect_timeout; } /* Check file descriptor */ addrlen = sizeof(status); if (getsockopt(thread->u.f.fd, SOL_SOCKET, SO_ERROR, (void *) &status, &addrlen) < 0) { /* getsockopt failed !!! */ thread_close_fd(thread); return connect_error; } /* If status = 0, TCP connection to remote host is established. * Otherwise register checker thread to handle connection in progress, * and other error code until connection is established. * Recompute the write timeout (or pending connection). */ if (status == 0) return connect_success; if (status == EINPROGRESS) { timer_min = timer_sub_now(thread->sands); thread_add_write(thread->master, func, THREAD_ARG(thread), thread->u.f.fd, -timer_long(timer_min), THREAD_DESTROY_CLOSE_FD | extra_flags); return connect_in_progress; } thread_close_fd(thread); if (status == ETIMEDOUT) return connect_timeout; /* Since the connect() call succeeded, treat this as a * failure to establish a connection. */ return connect_fail; } #ifdef _WITH_LVS_ bool socket_connection_state(int fd, enum connect_result status, thread_ref_t thread, thread_func_t func, unsigned long timeout, unsigned extra_flags) { if (status == connect_success || status == connect_in_progress) { thread_add_write(thread->master, func, THREAD_ARG(thread), fd, timeout, THREAD_DESTROY_CLOSE_FD | extra_flags); return false; } return true; } enum connect_result udp_bind_connect(int fd, conn_opts_t *co, uint8_t *payload, uint16_t payload_len) { socklen_t addrlen; ssize_t ret; const sockaddr_t *addr = &co->dst; const sockaddr_t *bind_addr = &co->bindto; char buf[UDP_BUFSIZE]; int on = 1; int err; /* Ensure we don't leak our stack */ if (!payload) { set_buf(buf, sizeof(buf)); payload = PTR_CAST(uint8_t, buf); payload_len = sizeof(buf); } /* We want to be able to receive ICMP error responses */ if (co->dst.ss_family == AF_INET) err = setsockopt(fd, SOL_IP, IP_RECVERR, PTR_CAST(char, &on), sizeof(on)); else err = setsockopt(fd, SOL_IPV6, IPV6_RECVERR, PTR_CAST(char, &on), sizeof(on)); if (err) log_message(LOG_INFO, "Error %d setting IP%s_RECVERR for socket %d - %m", errno, co->dst.ss_family == AF_INET ? "" : "V6", fd); #ifdef _WITH_SO_MARK_ if (co->fwmark) { if (setsockopt (fd, SOL_SOCKET, SO_MARK, &co->fwmark, sizeof (co->fwmark)) < 0) { log_message(LOG_ERR, "Error setting fwmark %u to socket: %s", co->fwmark, strerror(errno)); return connect_error; } } #endif /* Bind socket */ if (PTR_CAST_CONST(struct sockaddr, bind_addr)->sa_family != AF_UNSPEC) { addrlen = sizeof(*bind_addr); if (bind(fd, PTR_CAST_CONST(struct sockaddr, bind_addr), addrlen) != 0) { log_message(LOG_INFO, "bind failed. errno: %d, error: %s", errno, strerror(errno)); return connect_error; } } /* Set remote IP and connect */ addrlen = sizeof(*addr); ret = connect(fd, PTR_CAST_CONST(struct sockaddr, addr), addrlen); if (ret < 0) { /* We want to know about the error, but not repeatedly */ if (errno != co->last_errno) { co->last_errno = errno; if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "UDP connect error %d - %m", errno); } return connect_error; } /* Send udp packet */ ret = send(fd, payload, payload_len, 0); if (ret == payload_len) return connect_success; if (ret == -1) { /* We want to know about the error, but not repeatedly */ if (errno != co->last_errno) { co->last_errno = errno; if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "UDP send error %d - %m", errno); } } else if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "udp_bind_connect send - sent %zd bytes instead of %zu", ret, sizeof(buf)); return connect_error; } static enum connect_result udp_socket_error(int fd) { struct msghdr msg; char name_buf[128]; struct iovec iov; char control[2560] __attribute__((aligned(__alignof__(struct cmsghdr)))); struct icmphdr icmph; struct cmsghdr *cmsg; /* Control related data */ struct sock_extended_err *sock_err; ssize_t n; iov.iov_base = &icmph; iov.iov_len = sizeof icmph; msg.msg_name = name_buf; msg.msg_namelen = sizeof(name_buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof control; msg.msg_flags = 0; n = recvmsg(fd, &msg, MSG_ERRQUEUE); if (n == -1) { log_message(LOG_INFO, "udp_socket_error recvmsg failed - errno %d", errno); return connect_success; } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { sock_err = PTR_CAST(struct sock_extended_err, CMSG_DATA(cmsg)); if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) { if (sock_err) { /* We are interested in ICMP errors */ if (sock_err->ee_origin == SO_EE_ORIGIN_ICMP && sock_err->ee_type == ICMP_DEST_UNREACH) { #ifdef ICMP_DEBUG /* Handle ICMP errors types */ switch (sock_err->ee_code) { case ICMP_NET_UNREACH: /* Handle this error */ log_message(LOG_INFO, "Network Unreachable Error"); break; case ICMP_HOST_UNREACH: /* Handle this error */ log_message(LOG_INFO, "Host Unreachable Error"); break; case ICMP_PORT_UNREACH: /* Handle this error */ log_message(LOG_INFO, "Port Unreachable Error"); break; default: log_message(LOG_INFO, "Unreach code %d", sock_err->ee_code); } #endif return connect_error; #ifndef ICMP_DEBUG } } } #else } else log_message(LOG_INFO, "ee_origin %d, ee_type %d", sock_err->ee_origin, sock_err->ee_type); } else log_message(LOG_INFO, "No CMSG_DATA"); } #endif else if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR) { if (sock_err) { /* We are interested in ICMP errors */ if (sock_err->ee_origin == SO_EE_ORIGIN_ICMP6 && sock_err->ee_type == ICMPV6_DEST_UNREACH) { #ifdef ICMP_DEBUG /* Handle ICMP errors types */ switch (sock_err->ee_code) { case ICMPV6_NOROUTE: /* Handle this error */ log_message(LOG_INFO, "No Route Error"); break; case ICMPV6_ADDR_UNREACH: /* Handle this error */ log_message(LOG_INFO, "Address Unreachable Error"); break; case ICMPV6_PORT_UNREACH: /* Handle this error */ log_message(LOG_INFO, "Port Unreachable Error"); break; default: log_message(LOG_INFO, "Unreach code %d", sock_err->ee_code); } #endif return connect_error; #ifndef ICMP_DEBUG } } } #else } else log_message(LOG_INFO, "ee_origin %d, ee_type %d", sock_err->ee_origin, sock_err->ee_type); } else log_message(LOG_INFO, "No CMSG_DATA"); } else log_message(LOG_INFO, "cmsg_level %d, cmsg->type %d", cmsg->cmsg_level, cmsg->cmsg_type); #endif } return connect_success; } enum connect_result udp_socket_state(int fd, thread_ref_t thread, uint8_t *recv_buf, size_t *len) { int ret; char local_recv_buf; /* Handle Read timeout, we consider it success unless require_reply is set */ if (thread->type == THREAD_READ_TIMEOUT) return recv_buf ? connect_error : connect_success; if (thread->type == THREAD_READ_ERROR) return udp_socket_error(fd); if (recv_buf) { ret = recv(fd, recv_buf, *len, 0); *len = ret; } else { ret = recv(fd, &local_recv_buf, sizeof(local_recv_buf), 0); } /* Ret less than 0 means the port is unreachable. * Otherwise, we consider it success. */ if (ret < 0) return connect_error; return connect_success; } bool udp_icmp_check_state(int fd, enum connect_result status, thread_ref_t thread, thread_func_t func, unsigned long timeout) { checker_t *checker; checker = THREAD_ARG(thread); if (status == connect_success) { thread_add_read(thread->master, func, checker, fd, timeout, THREAD_DESTROY_CLOSE_FD); return false; } return true; } #endif keepalived-2.3.3/keepalived/core/config_notify.c0000664000175000017500000000677414115725161015376 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Config read completion notification * * Author: Quentin Armitage, * * 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) 2021-2021 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include "config_notify.h" #include "logger.h" #include "scheduler.h" #include "systemd.h" #include "main.h" #include "parser.h" #include "utils.h" static int child_reloaded_event = -1; static bool loaded; static bool reload_queued; void queue_reload(void) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Reload already in progress, request queued"); reload_queued = true; } static void child_reloaded_thread(__attribute__((unused)) thread_ref_t thread) { uint64_t event_count; int ret; ret = read(thread->u.f.fd, &event_count, sizeof(event_count)); if (ret != sizeof(event_count)) { log_message(LOG_INFO, "read eventfd returned %d, errno %d - %m", ret, errno); return; } if (num_reloading >= event_count) { num_reloading -= event_count; if (!num_reloading) { log_message(LOG_INFO, "%s complete", loaded ? "Reload" : "Startup"); loaded = true; #ifdef _USE_SYSTEMD_NOTIFY_ systemd_notify_running(); #endif if (reload_queued) { reload_queued = false; thread_add_event(master, start_reload, NULL, 0); } } } else log_message(LOG_INFO, "read eventfd count %" PRIu64 ", num_reloading %u", event_count, num_reloading); thread_add_read(master, child_reloaded_thread, NULL, child_reloaded_event, TIMER_NEVER, 0); } void open_config_read_fd(void) { child_reloaded_event = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); thread_add_read(master, child_reloaded_thread, NULL, child_reloaded_event, TIMER_NEVER, 0); } void notify_config_read(void) { uint64_t one = 1; /* If we are not the parent, tell it we have completed reading the configuration */ if (write(child_reloaded_event, &one, sizeof(one)) <= 0) log_message(LOG_INFO, "Write child_reloaded_event errno %d - %m", errno); } #ifndef _ONE_PROCESS_DEBUG_ void save_config(bool post, const char *process, void(*func)(FILE *)) { static unsigned reload_num = 0; FILE *file; char buf[128]; if (!config_save_dir) return; if (!post) reload_num++; sprintf(buf, "%s/keepalived_%s.%d.%u.%s", config_save_dir, process, getpid(), reload_num, post ? "post" : "pre"); file = fopen_safe(buf, "w"); if (!file) { log_message(LOG_INFO, "Failed to open config_save file %s", buf); return; } (*func)(file); fclose(file); } #endif #ifdef THREAD_DUMP void register_config_notify_addresses(void) { register_thread_address("child_reloaded_thread", child_reloaded_thread); register_thread_address("start_reload", start_reload); } #endif keepalived-2.3.3/keepalived/core/smtp.c0000664000175000017500000004447114555732065013531 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: SMTP WRAPPER connect to a specified smtp server and send mail * using the smtp protocol according to the RFC 821. A non blocking * timeouted connection is used to handle smtp protocol. * * Author: Alexandre Cassen, * * 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-2021 Alexandre Cassen, */ #include "config.h" #include #include #include #include "smtp.h" #include "memory.h" #include "layer4.h" #include "logger.h" #include "utils.h" #ifdef _WITH_LVS_ #include "check_api.h" #endif #ifdef THREAD_DUMP #include "scheduler.h" #endif /* If it suspected that one of subject, body or buffer * is overflowing in the smtp_t structure, defining SMTP_MSG_ALLOC_DEBUG * when using --enable-mem-check should help identity the issue */ //#define SMTP_MSG_ALLOC_DEBUG #define SMTP_BUFFER_MAX 1024U #ifdef _SMTP_ALERT_DEBUG_ bool do_smtp_alert_debug; #endif #ifdef _SMTP_CONNECT_DEBUG_ bool do_smtp_connect_debug; #endif static char smtp_send_buffer[SMTP_BUFFER_MAX]; /* SMTP FSM definition */ static void connection_error(thread_ref_t); static void connection_in_progress(thread_ref_t); static void connection_timeout(thread_ref_t); static void connection_success(thread_ref_t); static void helo_cmd(thread_ref_t); static void mail_cmd(thread_ref_t); static void rcpt_cmd(thread_ref_t); static void data_cmd(thread_ref_t); static void body_cmd(thread_ref_t); static void quit_cmd(thread_ref_t); static void rcpt_code(thread_ref_t); static void body_code(thread_ref_t); static void quit_code(thread_ref_t); static void smtp_read_thread(thread_ref_t); struct { void (*send) (thread_ref_t); void (*read) (thread_ref_t); int expected_code; const char *cmd_name; } SMTP_FSM[SMTP_MAX_FSM_STATE] = { /* Code Stream Write Handlers Stream Read handlers * *------------------------------+----------------------------------------------------*/ [connect_error] = {connection_error, NULL, -1, NULL}, [connect_in_progress] = {connection_in_progress, NULL, -1, NULL}, [connect_timeout] = {connection_timeout, NULL, -1, NULL}, [connect_fail] = {connection_error, NULL, -1, NULL}, [connect_success] = {connection_success, NULL, 220, "(E)SMTP"}, [HELO] = {helo_cmd, NULL, 250, "HELO"}, [MAIL] = {mail_cmd, NULL, 250, "MAIL"}, [RCPT] = {rcpt_cmd, rcpt_code, 250, "RCPT"}, [DATA] = {data_cmd, NULL, 354, "DATA"}, [BODY] = {body_cmd, body_code, 250, "BODY"}, [QUIT] = {quit_cmd, quit_code, 221, "QUIT"} }; static inline void free_smtp_msg_data(smtp_t * smtp) { #ifdef SMTP_MSG_ALLOC_DEBUG FREE(smtp->buffer); FREE(smtp->subject); FREE(smtp->body); #endif FREE(smtp); } static smtp_t * alloc_smtp_msg_data(void) { smtp_t *smtp; /* allocate & initialize smtp argument data structure */ #ifdef SMTP_MSG_ALLOC_DEBUG PMALLOC(smtp); smtp->subject = (char *)MALLOC(MAX_HEADERS_LENGTH); smtp->body = (char *)MALLOC(MAX_BODY_LENGTH); smtp->buffer = (char *)MALLOC(SMTP_BUFFER_MAX); #else smtp = MALLOC(sizeof(smtp_t) + MAX_HEADERS_LENGTH + MAX_BODY_LENGTH + SMTP_BUFFER_MAX + SMTP_BUFFER_MAX); smtp->subject = (char *)smtp + sizeof(smtp_t); smtp->body = smtp->subject + MAX_HEADERS_LENGTH; smtp->buffer = smtp->body + MAX_BODY_LENGTH; #endif return smtp; } static void smtp_send(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); smtp_send_buffer[0] = '\0'; SMTP_FSM_SEND(smtp->stage, thread); if (!smtp_send_buffer[0]) { /* Nothing in send buffer means connection failed */ thread_close_fd(thread); free_smtp_msg_data(smtp); return; } if (send(thread->u.f.fd, smtp_send_buffer, strlen(smtp_send_buffer), 0) == -1) { log_message(LOG_INFO, "Cannot send data to remote SMTP server %s." , FMT_SMTP_HOST()); thread_close_fd(thread); free_smtp_msg_data(smtp); return; } /* Registering next smtp command processing thread */ thread_add_read(thread->master, smtp_read_thread, smtp, thread->u.f.fd, global_data->smtp_connection_to, THREAD_DESTROY_CLOSE_FD | THREAD_DESTROY_FREE_ARG); } /* layer4 connection handlers */ static void connection_error(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); log_message(LOG_INFO, "SMTP connection ERROR to %s." , FMT_SMTP_HOST()); free_smtp_msg_data(smtp); } static void connection_timeout(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); log_message(LOG_INFO, "Timeout connecting SMTP server %s." , FMT_SMTP_HOST()); free_smtp_msg_data(smtp); } static void connection_in_progress(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); #ifdef _SMTP_CONNECT_DEBUG_ if (do_smtp_connect_debug) log_message(LOG_DEBUG, "SMTP connection to %s now IN_PROGRESS.", FMT_SMTP_HOST()); #endif if (thread->type == THREAD_WRITE_TIMEOUT) { log_message(LOG_INFO, "Timeout opening connection to remote SMTP server %s." , FMT_SMTP_HOST()); thread_close_fd(thread); free_smtp_msg_data(smtp); return; } else if (thread->type == THREAD_WRITE_ERROR) { log_message(LOG_INFO, "smtp fd %d returned write error", thread->u.f.fd); thread_close_fd(thread); free_smtp_msg_data(smtp); return; } /* * Here we use the propriety of a union structure, * each element of the structure have the same value. */ smtp->stage = tcp_socket_state(thread, connection_in_progress, THREAD_DESTROY_FREE_ARG); if (smtp->stage != connect_in_progress) { thread_del_write(thread); SMTP_FSM_SEND(smtp->stage, thread); } } static void connection_success(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Remote SMTP server %s connected." , FMT_SMTP_HOST()); smtp->stage = connect_success; thread_add_read(thread->master, smtp_read_thread, smtp, thread->u.f.fd, global_data->smtp_connection_to, THREAD_DESTROY_CLOSE_FD | THREAD_DESTROY_FREE_ARG); } /* SMTP protocol handlers */ static void smtp_read_thread(thread_ref_t thread) { smtp_t *smtp; char *reply; ssize_t rcv_buffer_size; int status = -1; smtp = THREAD_ARG(thread); if (thread->type == THREAD_READ_TIMEOUT) { log_message(LOG_INFO, "Timeout reading data to remote SMTP server %s." , FMT_SMTP_HOST()); if (smtp->stage == QUIT) { /* We have already sent a quit, so just terminate */ thread_close_fd(thread); free_smtp_msg_data(smtp); } else { smtp->stage = QUIT; smtp_send(thread); } return; } else if (thread->type == THREAD_READ_ERROR) { log_message(LOG_INFO, "smtp fd %d returned read error", thread->u.f.fd); thread_close_fd(thread); free_smtp_msg_data(smtp); return; } rcv_buffer_size = read(thread->u.f.fd, smtp->buffer + smtp->buflen, SMTP_BUFFER_MAX - smtp->buflen); if (rcv_buffer_size == -1) { if (check_EAGAIN(errno)) { thread_add_read(thread->master, smtp_read_thread, smtp, thread->u.f.fd, global_data->smtp_connection_to, THREAD_DESTROY_CLOSE_FD | THREAD_DESTROY_FREE_ARG); return; } log_message(LOG_INFO, "Error reading data from remote SMTP server %s." , FMT_SMTP_HOST()); smtp->stage = QUIT; smtp_send(thread); return; } if (rcv_buffer_size == 0) { log_message(LOG_INFO, "Remote SMTP server %s has closed the connection." , FMT_SMTP_HOST()); thread_close_fd(thread); free_smtp_msg_data(smtp); return; } /* received data overflow buffer size ? */ if (smtp->buflen + rcv_buffer_size >= SMTP_BUFFER_MAX) { log_message(LOG_INFO, "Received buffer from remote SMTP server %s" " overflow our get read buffer length." , FMT_SMTP_HOST()); smtp->buflen = 0; smtp->stage = QUIT; smtp_send(thread); return; } smtp->buflen += (size_t)rcv_buffer_size; smtp->buffer[smtp->buflen] = 0; /* NULL terminate */ /* parse the buffer, finding the last line of the response for the code */ reply = smtp->buffer; while (reply < smtp->buffer + smtp->buflen) { // This line causes a strict-overflow=4 warning with gcc 5.4.0 char *p; p = strstr(reply, "\r\n"); if (!p) { if (reply != smtp->buffer) { memmove(smtp->buffer, reply, smtp->buflen - (size_t)(reply - smtp->buffer) + 1); /* Include terminating NUL byte */ smtp->buflen -= (size_t)(reply - smtp->buffer); } thread_add_read(thread->master, smtp_read_thread, smtp, thread->u.f.fd, global_data->smtp_connection_to, THREAD_DESTROY_CLOSE_FD | THREAD_DESTROY_FREE_ARG); return; } /* Is it a multi-line reply? */ if (reply[3] == '-') { /* Skip over the \r\n */ reply = p + 2; continue; } status = ((reply[0] - '0') * 100) + ((reply[1] - '0') * 10) + (reply[2] - '0'); reply = p + 2; break; } if (reply >= smtp->buffer + smtp->buflen) smtp->buflen = 0; else { memmove(smtp->buffer, reply, smtp->buflen - (size_t)(reply - smtp->buffer) + 1); smtp->buflen -= (size_t)(reply - smtp->buffer); } if (status == -1) { thread_add_read(thread->master, smtp_read_thread, smtp, thread->u.f.fd, global_data->smtp_connection_to, THREAD_DESTROY_CLOSE_FD | THREAD_DESTROY_FREE_ARG); return; } if (status == SMTP_FSM[smtp->stage].expected_code) { if (SMTP_FSM[smtp->stage].read) SMTP_FSM_READ(smtp->stage, thread); else smtp->stage++; } else { /* Incorrect code returned */ if (SMTP_FSM[smtp->stage].cmd_name) log_message(LOG_INFO, "Error processing %s cmd on SMTP server %s." " SMTP status code = %d" , SMTP_FSM[smtp->stage].cmd_name , FMT_SMTP_HOST() , status); smtp->stage = QUIT; } if (smtp->stage == END) { /* We have finished */ thread_close_fd(thread); free_smtp_msg_data(smtp); return; } /* Send next packet */ smtp_send(thread); } /* HELO command processing */ static void helo_cmd(__attribute__((unused)) thread_ref_t thread) { snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "HELO %s\r\n", (global_data->smtp_helo_name) ? global_data->smtp_helo_name : "localhost"); } /* MAIL command processing */ static void mail_cmd(__attribute__((unused)) thread_ref_t thread) { size_t len; const char *start; len = strlen(global_data->email_from); if (global_data->email_from[len - 1] == '>' && (start = strrchr(global_data->email_from, '<'))) snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "MAIL FROM:%s\r\n", start); else snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "MAIL FROM:<%s>\r\n", global_data->email_from); } /* RCPT command processing */ static void rcpt_cmd(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); email_t *email = smtp->next_email_element; size_t len; const char *start; /* We send RCPT TO command multiple time to add all our email receivers. * --rfc821.3.1 */ if (list_is_last(&smtp->next_email_element->e_list, &global_data->email)) smtp->next_email_element = NULL; else smtp->next_email_element = list_entry(email->e_list.next, email_t, e_list); len = strlen(email->addr); if (email->addr[len - 1] == '>' && (start = strrchr(email->addr, '<'))) snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "RCPT TO:%s\r\n", start); else snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "RCPT TO:<%s>\r\n", email->addr); } static void rcpt_code(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); if (!smtp->next_email_element) smtp->stage++; } /* DATA command processing */ static void data_cmd(__attribute__((unused)) thread_ref_t thread) { strncpy(smtp_send_buffer, "DATA\r\n", sizeof(smtp_send_buffer)); } /* BODY command processing. * Do we need to use multi-thread for multi-part body * handling? Don't really think so :) */ static void body_cmd(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); char rfc822[80]; /* Mon, 01 Mar 2021 09:44:08 +0000 */ time_t now; struct tm t; size_t offs = 0; email_t *email; time(&now); localtime_r(&now, &t); strftime(rfc822, sizeof(rfc822), "%a, %d %b %Y %H:%M:%S %z", &t); /* send the DATA fields */ offs = snprintf(smtp_send_buffer, sizeof(smtp_send_buffer), "Date: %s\r\n" "From: %s\r\n" "Subject: %s\r\n" "X-Mailer: Keepalived\r\n" "To:", rfc822, global_data->email_from, smtp->subject); /* Add the recipients */ list_for_each_entry(email, &global_data->email, e_list) { offs += snprintf(smtp_send_buffer + offs, sizeof(smtp_send_buffer) - offs, "%s %s", list_is_first(&email->e_list, &global_data->email) ? "" : ",\r\n", email->addr); } /* Now the message body */ snprintf(smtp_send_buffer + offs, sizeof(smtp_send_buffer) - offs, "\r\n\r\n" "%s\r\n" "\r\n.\r\n", smtp->body); } static void body_code(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "SMTP alert successfully sent."); smtp->stage++; } /* QUIT command processing */ static void quit_cmd(__attribute__((unused)) thread_ref_t thread) { strncpy(smtp_send_buffer, "QUIT\r\n", sizeof(smtp_send_buffer)); } static void quit_code(thread_ref_t thread) { smtp_t *smtp = THREAD_ARG(thread); smtp->stage = END; } /* connect remote SMTP server */ static void smtp_connect(smtp_t *smtp) { enum connect_result status; int fd; smtp->next_email_element = list_first_entry(&global_data->email, email_t, e_list); if ((fd = socket(global_data->smtp_server.ss_family, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_TCP)) == -1) { #ifdef _SMTP_CONNECT_DEBUG_ if (do_smtp_connect_debug) log_message(LOG_DEBUG, "SMTP connect fail to create socket."); #endif free_smtp_msg_data(smtp); return; } status = tcp_connect(fd, &global_data->smtp_server); /* Handle connection status code */ if (status == connect_in_progress) { thread_add_write(master, connection_in_progress, smtp, fd, global_data->smtp_connection_to, THREAD_DESTROY_CLOSE_FD | THREAD_DESTROY_FREE_ARG); return; } if (status == connect_success) { thread_t thread = { .u.f.fd = fd, .master = master, .arg = smtp }; connection_success(&thread); return; } /* connect_fail, connect_error */ close(fd); free_smtp_msg_data(smtp); } #ifdef _SMTP_ALERT_DEBUG_ static void smtp_log_to_file(smtp_t *smtp) { FILE *fp; time_t now; struct tm tm; char time_buf[25]; int time_buf_len; const char *file_name; email_t *email; file_name = make_tmp_filename("smtp-alert.log"); fp = fopen_safe(file_name, "a"); FREE_CONST(file_name); if (fp) { time(&now); localtime_r(&now, &tm); time_buf_len = strftime(time_buf, sizeof time_buf, "%a %b %e %X %Y", &tm); fprintf(fp, "%s: %s ->", time_buf, global_data->email_from); list_for_each_entry(email, &global_data->email, e_list) fprintf(fp, "%s %s", list_is_first(&email->e_list, &global_data->email) ? "" : ",", email->addr); fprintf(fp, "\n" "%*sSubject: %s\n" "%*sBody: %s\n\n", time_buf_len - 7, "", smtp->subject, time_buf_len - 7, "", smtp->body); fclose(fp); } free_smtp_msg_data(smtp); } #endif /* Main entry point */ void smtp_alert(smtp_msg_t msg_type, void* data, const char *subject, const char *body) { smtp_t *smtp; #ifdef _WITH_VRRP_ vrrp_t *vrrp; vrrp_sgroup_t *vgroup; #endif #ifdef _WITH_LVS_ checker_t *checker; virtual_server_t *vs; smtp_rs *rs_info; #endif /* Only send mail if email specified */ if (list_empty(&global_data->email) || !global_data->smtp_server.ss_family) return; /* allocate & initialize smtp argument data structure */ smtp = alloc_smtp_msg_data(); /* format subject if rserver is specified */ #ifdef _WITH_LVS_ if (msg_type == SMTP_MSG_RS) { checker = PTR_CAST(checker_t, data); snprintf(smtp->subject, MAX_HEADERS_LENGTH, "[%s] Realserver %s of virtual server %s - %s", global_data->router_id, FMT_RS(checker->rs, checker->vs), FMT_VS(checker->vs), checker->rs->alive ? "UP" : "DOWN"); } else if (msg_type == SMTP_MSG_VS) { vs = PTR_CAST(virtual_server_t, data); snprintf(smtp->subject, MAX_HEADERS_LENGTH, "[%s] Virtualserver %s - %s", global_data->router_id, FMT_VS(vs), subject); } else if (msg_type == SMTP_MSG_RS_SHUT) { rs_info = PTR_CAST(smtp_rs, data); snprintf(smtp->subject, MAX_HEADERS_LENGTH, "[%s] Realserver %s of virtual server %s - %s", global_data->router_id, FMT_RS(rs_info->rs, rs_info->vs), FMT_VS(rs_info->vs), subject); } else #endif #ifdef _WITH_VRRP_ if (msg_type == SMTP_MSG_VRRP) { vrrp = PTR_CAST(vrrp_t, data); snprintf(smtp->subject, MAX_HEADERS_LENGTH, "[%s] VRRP Instance %s - %s", global_data->router_id, vrrp->iname, subject); } else if (msg_type == SMTP_MSG_VGROUP) { vgroup = PTR_CAST(vrrp_sgroup_t, data); snprintf(smtp->subject, MAX_HEADERS_LENGTH, "[%s] VRRP Group %s - %s", global_data->router_id, vgroup->gname, subject); } else #endif if (global_data->router_id) snprintf(smtp->subject, MAX_HEADERS_LENGTH, "[%s] %s" , global_data->router_id , subject); else snprintf(smtp->subject, MAX_HEADERS_LENGTH, "%s", subject); strncpy(smtp->body, body, MAX_BODY_LENGTH - 1); smtp->body[MAX_BODY_LENGTH - 1]= '\0'; #ifdef _SMTP_ALERT_DEBUG_ if (do_smtp_alert_debug) smtp_log_to_file(smtp); else #endif smtp_connect(smtp); } #ifdef THREAD_DUMP void register_smtp_addresses(void) { register_thread_address("body_cmd", body_cmd); register_thread_address("connection_error", connection_error); register_thread_address("connection_in_progress", connection_in_progress); register_thread_address("connection_success", connection_success); register_thread_address("connection_timeout", connection_timeout); register_thread_address("data_cmd", data_cmd); register_thread_address("helo_cmd", helo_cmd); register_thread_address("mail_cmd", mail_cmd); register_thread_address("quit_cmd", quit_cmd); register_thread_address("rcpt_cmd", rcpt_cmd); register_thread_address("smtp_read_thread", smtp_read_thread); } #endif keepalived-2.3.3/keepalived/core/Makefile.am0000664000175000017500000000245014645450427014424 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2020 Alexandre Cassen, AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib AM_CPPFLAGS += $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) -DLOCAL_STATE_DIR=\"@localstatedir@\" AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libcore.a libcore_a_SOURCES = main.c daemon.c pidfile.c layer4.c smtp.c \ global_data.c global_parser.c keepalived_netlink.c \ namespaces.c libcore_a_LIBADD = EXTRA_libcore_a_SOURCES = if SNMP libcore_a_LIBADD += snmp.o EXTRA_libcore_a_SOURCES += snmp.c endif if NFTABLES libcore_a_LIBADD += nftables.o EXTRA_libcore_a_SOURCES += nftables.c endif if LIBNL_DYNAMIC libcore_a_LIBADD += libnl_link.o EXTRA_libcore_a_SOURCES += libnl_link.c endif if TRACK_PROCESS libcore_a_LIBADD += track_process.o EXTRA_libcore_a_SOURCES += track_process.c endif if !ONE_PROCESS_DEBUG libcore_a_LIBADD += reload_monitor.o config_notify.o EXTRA_libcore_a_SOURCES += reload_monitor.c config_notify.c endif if WITH_SANITIZER libcore_a_LIBADD += sanitizer.o EXTRA_libcore_a_SOURCES += sanitizer.c endif MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ keepalived-2.3.3/keepalived/core/daemon.c0000664000175000017500000000341313717517573014004 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Main program structure. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include "daemon.h" #include "logger.h" #include "utils.h" /* Daemonization function coming from zebra source code */ pid_t xdaemon(void) { pid_t pid; #ifdef ENABLE_LOG_TO_FILE if (log_file_name) flush_log_file(); #endif /* In case of fork is error. */ pid = fork(); if (pid < 0) { log_message(LOG_INFO, "xdaemon: fork error"); return -1; } /* In case of this is parent process. */ if (pid != 0) return pid; /* Become session leader and get pid. */ if (setsid() < 0) { log_message(LOG_INFO, "xdaemon: setsid error"); return -1; } /* Change directory to root. */ if (chdir("/") < 0) log_message(LOG_INFO, "xdaemon: chdir error"); /* File descriptor close. */ set_std_fd(true); return 0; } keepalived-2.3.3/keepalived/core/keepalived_netlink.c0000664000175000017500000022745214771471130016376 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: NETLINK kernel command channel. * * Author: Alexandre Cassen, * * 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, */ /* To monitor netlink messages and decode them: * ip link add nlmon0 type nlmon * ip link set nlmon0 up * tcpdump -i nlmon0 -w OP_FILE * wireshare OP_FILE */ #include "config.h" /* global include */ #include #include #include #include #include #include #include #include #include #include #ifdef _WITH_VRRP_ #include #include #include #include #endif #include #include #ifdef THREAD_DUMP #include "scheduler.h" #endif /* local include */ #include "keepalived_netlink.h" #ifdef _WITH_LVS_ #include "check_api.h" #endif #ifdef _WITH_VRRP_ #include "vrrp_scheduler.h" #include "vrrp_track.h" #include "vrrp_data.h" #include "vrrp_if.h" #ifdef _HAVE_VRRP_VMAC_ #include "vrrp_vmac.h" #endif #ifdef _WITH_VRRP_ #include "vrrp_iproute.h" #include "vrrp_iprule.h" #endif #endif #include "logger.h" #include "scheduler.h" #include "utils.h" #include "list_head.h" #include "bitops.h" #include "vrrp_ipaddress.h" #include "global_data.h" #include "align.h" #include "warnings.h" /* This seems a nasty hack, but it's what iproute2 does */ #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif /* Default values */ #define IF_DEFAULT_BUFSIZE (64*1024) /* Global vars */ nl_handle_t nl_cmd = { .fd = -1 }; /* Command channel */ #ifdef _WITH_VRRP_ int netlink_error_ignore; /* If we get this error, ignore it */ #endif /* Static vars */ static nl_handle_t nl_kernel = { .fd = -1 }; /* Kernel reflection channel */ #ifdef _NETLINK_TIMERS_ /* The maximum netlink command we use is RTM_DELRULE. * If that changes, the following definition will need changing. */ #define MAX_NETLINK_TIMER RTM_DELRULE static struct timeval netlink_times[MAX_NETLINK_TIMER+1]; static unsigned netlink_count[MAX_NETLINK_TIMER+1]; #ifdef _WITH_VRRP_ static struct timeval start_time, end_time; #endif bool do_netlink_timers; #endif #ifdef _NETLINK_TIMERS_ void report_and_clear_netlink_timers(const char * str) { int i; log_message(LOG_INFO, "Netlink timers - %s", str); for (i = 0; i <= MAX_NETLINK_TIMER; i++) { if (netlink_count[i]) { log_message(LOG_INFO, " netlink cmd %d (%u calls), time %" PRI_tv_sec ".%6.6" PRI_tv_usec, i, netlink_count[i], netlink_times[i].tv_sec, netlink_times[i].tv_usec); netlink_times[i].tv_sec = netlink_times[i].tv_usec = netlink_count[i] = 0; } } } #endif static const char * get_nl_msg_type(unsigned type) { switch (type) { switch_define_str(RTM_NEWLINK); switch_define_str(RTM_DELLINK); switch_define_str(RTM_NEWADDR); switch_define_str(RTM_DELADDR); switch_define_str(RTM_NEWROUTE); switch_define_str(RTM_DELROUTE); switch_define_str(RTM_NEWRULE); switch_define_str(RTM_DELRULE); switch_define_str(RTM_GETLINK); switch_define_str(RTM_GETADDR); }; return ""; } static inline bool addr_is_equal2(struct ifaddrmsg* ifa, void* addr, ip_address_t* vip_addr, interface_t *ifp, vrrp_t *vrrp) { struct in_addr* sin_addr; struct in6_addr* sin6_addr; /* If vrrp is specified, we also want to make sure the matching address isn't * being added to the base interface of the vrrp instance */ if (vip_addr->ifa.ifa_family != ifa->ifa_family) return false; if (vip_addr->ifp != ifp && !(vrrp && vrrp->ifp && vip_addr->ifp == vrrp->ifp && VRRP_CONFIGURED_IFP(vrrp) == ifp)) return false; if (vip_addr->ifa.ifa_family == AF_INET) { sin_addr = PTR_CAST(struct in_addr, addr); return vip_addr->u.sin.sin_addr.s_addr == sin_addr->s_addr; } sin6_addr = PTR_CAST(struct in6_addr, addr); return vip_addr->u.sin6_addr.s6_addr32[0] == sin6_addr->s6_addr32[0] && vip_addr->u.sin6_addr.s6_addr32[1] == sin6_addr->s6_addr32[1] && vip_addr->u.sin6_addr.s6_addr32[2] == sin6_addr->s6_addr32[2] && vip_addr->u.sin6_addr.s6_addr32[3] == sin6_addr->s6_addr32[3]; } static inline bool addr_is_equal(struct ifaddrmsg* ifa, void* addr, ip_address_t* vip_addr, interface_t *ifp) { return addr_is_equal2(ifa, addr, vip_addr, ifp, NULL); } #ifdef _WITH_VRRP_ static vrrp_t * __attribute__ ((pure)) address_is_ours(struct ifaddrmsg *ifa, struct in_addr *addr, interface_t *ifp) { tracking_obj_t *top; vrrp_t *vrrp; ip_address_t *ip_addr; list_head_t *vip_list; list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; /* If we are not master, then we won't have the address configured */ if (vrrp->state != VRRP_STATE_MAST) continue; for (vip_list = ifa->ifa_family == vrrp->family ? &vrrp->vip : &vrrp->evip; vip_list; vip_list = vip_list == &vrrp->vip ? &vrrp->evip : NULL) { list_for_each_entry(ip_addr, vip_list, e_list) { if (addr_is_equal(ifa, addr, ip_addr, ifp) && ifa->ifa_prefixlen == ip_addr->ifa.ifa_prefixlen) return ip_addr->dont_track ? NULL : vrrp; } } } return NULL; } static bool __attribute__ ((pure)) ignore_address_if_ours_or_link_local(struct ifaddrmsg *ifa, struct in_addr *addr, interface_t *ifp) { tracking_obj_t *top; vrrp_t *vrrp; ip_address_t *ip_addr; /* We are only interested in link local for IPv6 */ if (ifa->ifa_family == AF_INET6 && ifa->ifa_scope != RT_SCOPE_LINK) return true; list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (ifa->ifa_family == vrrp->family) { list_for_each_entry(ip_addr, &vrrp->vip, e_list) { if (addr_is_equal2(ifa, addr, ip_addr, ifp, vrrp)) return true; } } list_for_each_entry(ip_addr, &vrrp->evip, e_list) { if (addr_is_equal2(ifa, addr, ip_addr, ifp, vrrp)) return true; } } return false; } #ifdef _WITH_VRRP_ static bool compare_addr(int family, void *addr1, ip_address_t *addr2) { union { struct in_addr *in; struct in6_addr *in6; } addr1_p = { .in = addr1 }; if (family == AF_INET) return addr1_p.in->s_addr != addr2->u.sin.sin_addr.s_addr; return addr1_p.in6->s6_addr32[0] != addr2->u.sin6_addr.s6_addr32[0] || addr1_p.in6->s6_addr32[1] != addr2->u.sin6_addr.s6_addr32[1] || addr1_p.in6->s6_addr32[2] != addr2->u.sin6_addr.s6_addr32[2] || addr1_p.in6->s6_addr32[3] != addr2->u.sin6_addr.s6_addr32[3]; } static bool compare_route(struct rtattr *tb[RTA_MAX + 1], ip_route_t *route, uint32_t table, int family, int mask_len, uint32_t priority, uint8_t tos) { union { struct in_addr in; struct in6_addr in6; } default_addr; if (table != route->table || family != route->family || mask_len != route->dst->ifa.ifa_prefixlen || priority != route->metric || tos != route->tos) return false; if (route->oif) { if (!tb[RTA_OIF] || route->oif->ifindex != *PTR_CAST(uint32_t, RTA_DATA(tb[RTA_OIF]))) return false; } else if (route->set) { if (!tb[RTA_OIF] != !route->configured_ifindex) return false; if (tb[RTA_OIF] && route->configured_ifindex != *PTR_CAST(uint32_t, RTA_DATA(tb[RTA_OIF]))) return false; } if (!tb[RTA_DST]) memset(&default_addr, 0, sizeof(default_addr)); if (compare_addr(family, tb[RTA_DST] ? RTA_DATA(tb[RTA_DST]) : &default_addr, route->dst)) return false; return true; } static ip_route_t * route_is_ours(struct rtmsg* rt, struct rtattr *tb[RTA_MAX + 1], vrrp_t** ret_vrrp) { uint32_t table; int family; int mask_len = rt->rtm_dst_len; uint32_t priority = 0; uint8_t tos = rt->rtm_tos; vrrp_t *vrrp; ip_route_t *route; *ret_vrrp = NULL; table = tb[RTA_TABLE] ? *PTR_CAST(uint32_t, RTA_DATA(tb[RTA_TABLE])) : rt->rtm_table; family = rt->rtm_family; if (tb[RTA_PRIORITY]) priority = *PTR_CAST(uint32_t, RTA_DATA(tb[RTA_PRIORITY])); list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { list_for_each_entry(route, &vrrp->vroutes, e_list) { if (compare_route(tb, route, table, family, mask_len, priority, tos)) { *ret_vrrp = vrrp; return route; } } } /* Now check the static routes */ list_for_each_entry(route, &vrrp_data->static_routes, e_list) { if (compare_route(tb, route, table, family, mask_len, priority, tos)) return route; } return NULL; } static bool compare_rule(struct fib_rule_hdr *frh, struct rtattr *tb[FRA_MAX + 1], ip_rule_t *rule) { if (rule->dont_track) return false; if (rule->family != frh->family) return false; /* This is a very good descriminator, since our rules will always have a priority */ if (!tb[FRA_PRIORITY] || rule->priority != *PTR_CAST(uint32_t, RTA_DATA(tb[FRA_PRIORITY]))) return false; if (frh->action != rule->action) return false; if (frh->action == FR_ACT_GOTO && (!tb[FRA_GOTO] || *PTR_CAST(uint32_t, RTA_DATA(tb[FRA_GOTO])) != rule->goto_target)) return false; if (tb[FRA_TABLE] && rule->table != *PTR_CAST(uint32_t, RTA_DATA(tb[FRA_TABLE]))) return false; if (!tb[FRA_TABLE] && rule->table != frh->table) return false; if (!rule->invert != !((frh->flags & FIB_RULE_INVERT))) return false; if (!rule->from_addr != !tb[FRA_SRC]) return false; if (rule->from_addr) { if (frh->src_len != rule->from_addr->ifa.ifa_prefixlen) return false; if (compare_addr(rule->family, RTA_DATA(tb[FRA_SRC]), rule->from_addr)) return false; } if (!rule->to_addr != !tb[FRA_DST]) return false; if (rule->to_addr) { if (frh->dst_len != rule->to_addr->ifa.ifa_prefixlen) return false; if (compare_addr(rule->family, RTA_DATA(tb[FRA_DST]), rule->to_addr)) return false; } if (rule->tos != frh->tos) return false; if (!tb[FRA_FWMARK] != !(rule->mask & IPRULE_BIT_FWMARK)) return false; if (rule->mask & IPRULE_BIT_FWMARK && *PTR_CAST(uint32_t, RTA_DATA(tb[FRA_FWMARK])) != rule->fwmark) return false; if (!tb[FRA_FWMASK] && (rule->mask & IPRULE_BIT_FWMASK)) return false; if (rule->mask & IPRULE_BIT_FWMASK) { if (*PTR_CAST(uint32_t, RTA_DATA(tb[FRA_FWMASK])) != rule->fwmask) return false; } else if (tb[FRA_FWMASK]) { if (*PTR_CAST(uint32_t, RTA_DATA(tb[FRA_FWMASK])) != 0xffffffff) return false; } if (!tb[FRA_FLOW] != !rule->realms) return false; if (rule->realms && *PTR_CAST(uint32_t, RTA_DATA(tb[FRA_FLOW])) != rule->realms) return false; #if HAVE_DECL_FRA_SUPPRESS_PREFIXLEN if (!tb[FRA_SUPPRESS_PREFIXLEN]) { if (rule->suppress_prefix_len != -1) return false; } else if (*PTR_CAST(int32_t, RTA_DATA(tb[FRA_SUPPRESS_PREFIXLEN])) != rule->suppress_prefix_len) return false; #endif #if HAVE_DECL_FRA_SUPPRESS_IFGROUP if (!tb[FRA_SUPPRESS_IFGROUP] != !(rule->mask & IPRULE_BIT_SUP_GROUP)) return false; if (rule->mask & IPRULE_BIT_SUP_GROUP && *PTR_CAST(uint32_t, RTA_DATA(tb[FRA_SUPPRESS_IFGROUP])) != rule->suppress_group) return false; #endif if (!tb[FRA_IFNAME] != !(rule->iif)) return false; if (rule->iif && strcmp(RTA_DATA(tb[FRA_IFNAME]), rule->iif->ifname)) return false; if (!tb[FRA_OIFNAME] != !(rule->oif)) return false; if (rule->oif && strcmp(RTA_DATA(tb[FRA_OIFNAME]), rule->oif->ifname)) return false; #if HAVE_DECL_FRA_TUN_ID uint64_t tunnel_id; if (!tb[FRA_TUN_ID] != !(rule->tunnel_id)) return false; if (rule->tunnel_id) { tunnel_id = be64toh(*PTR_CAST(uint64_t, RTA_DATA(tb[FRA_TUN_ID]))); if (tunnel_id != rule->tunnel_id) return false; } #endif #if HAVE_DECL_FRA_UID_RANGE if (!tb[FRA_UID_RANGE] != !(rule->mask & IPRULE_BIT_UID_RANGE)) return false; if ((rule->mask & IPRULE_BIT_UID_RANGE) && memcmp(RTA_DATA(tb[FRA_UID_RANGE]), &rule->uid_range, sizeof rule->uid_range)) return false; #endif #if HAVE_DECL_FRA_L3MDEV if (!tb[FRA_L3MDEV] && rule->l3mdev) return false; if (tb[FRA_L3MDEV] && *PTR_CAST(uint8_t, RTA_DATA(tb[FRA_L3MDEV])) != rule->l3mdev) return false; #endif #if HAVE_DECL_FRA_IP_PROTO if (!tb[FRA_IP_PROTO] != !(rule->mask & IPRULE_BIT_IP_PROTO)) return false; if (rule->mask & IPRULE_BIT_IP_PROTO && *PTR_CAST(uint8_t, RTA_DATA(tb[FRA_IP_PROTO])) != rule->ip_proto) return false; #endif #if HAVE_DECL_FRA_SPORT_RANGE if (!tb[FRA_SPORT_RANGE] != !(rule->mask & IPRULE_BIT_SPORT_RANGE)) return false; if (rule->mask & IPRULE_BIT_SPORT_RANGE && memcmp(RTA_DATA(tb[FRA_SPORT_RANGE]), &rule->src_port, sizeof rule->src_port)) return false; #endif #if HAVE_DECL_FRA_DPORT_RANGE if (!tb[FRA_DPORT_RANGE] != !(rule->mask & IPRULE_BIT_DPORT_RANGE)) return false; if (rule->mask & IPRULE_BIT_DPORT_RANGE && memcmp(RTA_DATA(tb[FRA_DPORT_RANGE]), &rule->dst_port, sizeof rule->dst_port)) return false; #endif return true; } static ip_rule_t * rule_is_ours(struct fib_rule_hdr* frh, struct rtattr *tb[FRA_MAX + 1], vrrp_t **ret_vrrp) { vrrp_t *vrrp; ip_rule_t *rule; *ret_vrrp = NULL; list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) { list_for_each_entry(rule, &vrrp->vrules, e_list) { if (compare_rule(frh, tb, rule)) { *ret_vrrp = vrrp; return rule; } } } list_for_each_entry(rule, &vrrp_data->static_rules, e_list) { if (compare_rule(frh, tb, rule)) return rule; } return NULL; } #endif #endif /* Update the netlink socket receive buffer sizes */ static void netlink_set_rx_buf_size(nl_handle_t *nl, unsigned rcvbuf_size, bool force) { if (!rcvbuf_size) rcvbuf_size = IF_DEFAULT_BUFSIZE; /* Set rcvbuf size */ if (force) { if (setsockopt(nl->fd, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf_size, sizeof(rcvbuf_size)) < 0) log_message(LOG_INFO, "cant set SO_RCVBUFFORCE IP option. errno=%d (%m)", errno); } else { if (setsockopt(nl->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(rcvbuf_size)) < 0) log_message(LOG_INFO, "Cannot set SO_RCVBUF IP option. errno=%d (%m)", errno); } } #ifdef _WITH_VRRP_ static void kernel_netlink_set_membership(int group, bool add) { if (setsockopt(nl_kernel.fd, SOL_NETLINK, add ? NETLINK_ADD_MEMBERSHIP : NETLINK_DROP_MEMBERSHIP, &group, sizeof(group)) < 0) log_message(LOG_INFO, "Netlink: Cannot add membership on netlink socket : (%s)", strerror(errno)); } void set_extra_netlink_monitoring(bool ipv4_routes, bool ipv6_routes, bool ipv4_rules, bool ipv6_rules) { kernel_netlink_set_membership(RTNLGRP_IPV4_ROUTE, ipv4_routes); kernel_netlink_set_membership(RTNLGRP_IPV6_ROUTE, ipv6_routes); kernel_netlink_set_membership(RTNLGRP_IPV4_RULE, ipv4_rules); kernel_netlink_set_membership(RTNLGRP_IPV6_RULE, ipv6_rules); } #endif /* Create a socket to netlink interface_t */ static void netlink_socket(nl_handle_t *nl, unsigned rcvbuf_size, bool force, int flags, unsigned group, ...) { int ret; va_list gp; memset(nl, 0, sizeof (*nl)); socklen_t addr_len; struct sockaddr_nl snl; int sock_flags = flags; nl->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | sock_flags, NETLINK_ROUTE); if (nl->fd < 0) { log_message(LOG_INFO, "Netlink: Cannot open netlink socket : (%s)", strerror(errno)); return; } memset(&snl, 0, sizeof (snl)); snl.nl_family = AF_NETLINK; ret = bind(nl->fd, PTR_CAST(struct sockaddr, &snl), sizeof (snl)); if (ret < 0) { log_message(LOG_INFO, "Netlink: Cannot bind netlink socket : (%s)", strerror(errno)); close(nl->fd); nl->fd = -1; return; } /* Join the requested groups */ va_start(gp, group); while (group) { ret = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)); if (ret < 0) log_message(LOG_INFO, "Netlink: Cannot add group %u membership on netlink socket : (%s)", group, strerror(errno)); group = va_arg(gp, unsigned); } va_end(gp); addr_len = sizeof (snl); ret = getsockname(nl->fd, PTR_CAST(struct sockaddr, &snl), &addr_len); if (ret < 0 || addr_len != sizeof (snl)) { log_message(LOG_INFO, "Netlink: Cannot getsockname : (%s)", strerror(errno)); close(nl->fd); nl->fd = -1; return; } if (snl.nl_family != AF_NETLINK) { log_message(LOG_INFO, "Netlink: Wrong address family %d", snl.nl_family); close(nl->fd); nl->fd = -1; return; } /* Save the port id for checking message source later */ nl->nl_pid = snl.nl_pid; #ifdef _INCLUDE_UNUSED_CODE_ /* There appears to be a kernel bug that manifests itself when we have a large number * of VMAC interfaces to add (i.e. 200 or more). After approx 200 interfaces have been * added the kernel will return ENOBUFS on the nl_kernel socket, and then repeat the * first 30 or so RTM_NEWLINK messages, omitting the first one. Then, at the end of * creating all the interfaces, i.e. after a slight delay with no new messages, * we get another ENOBUFS and all the RTM_NEWLINK messages from the time of the * first ENOBUFS message repeated. * * This problem also happens if the system already has a large (e.g. 200 or more) * number of interfaces configured before keepalived starts. * * This problem feels as though a circular buffer is wrapping around, and causes * all the old messages in the buffer to be resent, but the first one is omitted. * Note that it is only the interfaces that keepalived creates that are resent, * not interfaces that already existed on the system before keepalived starts. * * We can also get ENOBUFS on the nl_cmd socket if the NLM_F_ECHO flag is set as well as * the NLM_F_ACK flag when a command is sent on the nl_cmd socket. * * It appears that this must be a kernel bug, since when it happens on interface creation, * if we are also running `ip -ts monitor link addr route`, i.e. the same as the nl_kernel * socket, then precisely the same messages are repeated (provided we have set the * vrrp_netlink_cmd_rcv_bufs global configuration option to 1048576 (1024k) to match what * ip monitor does). */ int one = 1; if ((ret = setsockopt(nl->fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &one, sizeof(one))) < 0) log_message(LOG_INFO, "Cannot set NETLINK_NO_ENOBUFS option. errno=%d (%m)", errno); #endif nl->seq = (uint32_t)time(NULL); if (nl->fd < 0) return; netlink_set_rx_buf_size(nl, rcvbuf_size, force); } /* Close a netlink socket */ static void netlink_close(nl_handle_t *nl) { if (!nl) return; /* First of all release pending thread. There is no thread * for nl_cmd since it is used synchronously. */ if (nl->thread) { thread_cancel(nl->thread); nl->thread = NULL; } if (nl->fd != -1) close(nl->fd); nl->fd = -1; } /* iproute2 utility function */ /* GCC, at least up to v11.1.1, ignores the RELAX_STRINGOP_OVERFLOW below, * and produces warnings when doing the LTO link of vrrp_vmac.o and vrrp_ipaddress.o. * This should be tested periodically to see if specifying noinline can be removed. */ int GCC_LTO_NOINLINE addattr_l(struct nlmsghdr *n, size_t maxlen, unsigned short type, const void *data, size_t alen) { unsigned short len = RTA_LENGTH(alen); uint32_t align_len = RTA_SPACE(alen); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + align_len > maxlen) return -1; rta = PTR_CAST(struct rtattr, NLMSG_TAIL(n)); rta->rta_type = type; rta->rta_len = len; RELAX_STRINGOP_OVERFLOW if (alen) memcpy(RTA_DATA(rta), data, alen); RELAX_STRINGOP_OVERFLOW_END n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + align_len; return 0; } #ifdef _WITH_VRRP_ int addattr_l2(struct nlmsghdr *n, size_t maxlen, unsigned short type, const void *data, size_t alen, const void *data2, size_t alen2) { unsigned short len = RTA_LENGTH(alen); uint32_t align_len = RTA_SPACE(alen + alen2); struct rtattr *rta; if (NLMSG_ALIGN(n->nlmsg_len) + align_len > maxlen) return -1; rta = PTR_CAST(struct rtattr, NLMSG_TAIL(n)); rta->rta_type = type; rta->rta_len = len; if (alen) memcpy(RTA_DATA(rta), data, alen); if (alen2) memcpy((char *)RTA_DATA(rta) + alen, data2, alen2); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + align_len; return 0; } int addraw_l(struct nlmsghdr *n, size_t maxlen, const void *data, size_t len) { uint32_t align_len = NLMSG_ALIGN(len); if (NLMSG_ALIGN(n->nlmsg_len) + align_len > maxlen) return -1; memcpy(NLMSG_TAIL(n), data, len); if (align_len > len) memset(PTR_CAST(char, NLMSG_TAIL(n)) + len, 0, align_len - len); n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + align_len; return 0; } size_t rta_addattr_l(struct rtattr *rta, size_t maxlen, unsigned short type, const void *data, size_t alen) { struct rtattr *subrta; unsigned short len = RTA_LENGTH(alen); unsigned short align_len = RTA_SPACE(alen); if (RTA_ALIGN(rta->rta_len) + align_len > maxlen) return 0; subrta = PTR_CAST(struct rtattr, (char *)rta + RTA_ALIGN(rta->rta_len)); subrta->rta_type = type; subrta->rta_len = len; if (alen) memcpy(RTA_DATA(subrta), data, alen); rta->rta_len = RTA_ALIGN(rta->rta_len) + align_len; return align_len; } size_t rta_addattr_l2(struct rtattr *rta, size_t maxlen, unsigned short type, const void *data, size_t alen, const void *data2, size_t alen2) { struct rtattr *subrta; unsigned short len = RTA_LENGTH(alen + alen2); unsigned short align_len = RTA_ALIGN(len); if (RTA_ALIGN(rta->rta_len) + align_len > maxlen) return 0; subrta = PTR_CAST(struct rtattr, (char*)rta + RTA_ALIGN(rta->rta_len)); subrta->rta_type = type; subrta->rta_len = len; if (alen) memcpy(RTA_DATA(subrta), data, alen); if (alen2) memcpy((char *)RTA_DATA(subrta) + alen, data2, alen2); rta->rta_len = RTA_ALIGN(rta->rta_len) + align_len; return align_len; } struct rtattr * rta_nest(struct rtattr *rta, size_t maxlen, unsigned short type) { struct rtattr *nest = RTA_TAIL(rta); rta_addattr_l(rta, maxlen, type, NULL, 0); return nest; } size_t rta_nest_end(struct rtattr *rta, struct rtattr *nest) { nest->rta_len = (unsigned short)((char *)RTA_TAIL(rta) - (char *)nest); return rta->rta_len; } #endif static void parse_rtattr(struct rtattr **tb, int max, struct rtattr *rta, size_t len) { memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); while (RTA_OK(rta, len)) { if (rta->rta_type <= max) tb[rta->rta_type] = rta; /* Note: clang issues a -Wcast-align warning for RTA_NEXT, whereas gcc does not. * gcc is more clever in it's analysis, and realises that RTA_NEXT is actually * forcing alignment. */ rta = RTA_NEXT(rta, len); } } #ifdef _WITH_VRRP_ static void parse_rtattr_nested(struct rtattr **tb, int max, struct rtattr *rta) { parse_rtattr(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta)); } static void set_vrrp_backup(vrrp_t *vrrp) { vrrp_t *isync; vrrp->wantstate = VRRP_STATE_BACK; vrrp_state_leave_master(vrrp, true); if (vrrp->sync) { list_for_each_entry(isync, &vrrp->sync->vrrp_instances, s_list) { if (isync->state == VRRP_STATE_MAST) { isync->wantstate = VRRP_STATE_BACK; vrrp_state_leave_master(isync, true); /* We want a quick transition back to master */ isync->ms_down_timer = VRRP_TIMER_SKEW(isync); vrrp_init_instance_sands(isync); vrrp_thread_requeue_read(isync); } } vrrp->sync->state = VRRP_STATE_BACK; } /* We want a quick transition back to master */ vrrp->ms_down_timer = VRRP_TIMER_SKEW(vrrp); vrrp_init_instance_sands(vrrp); vrrp_thread_requeue_read(vrrp); } /* Check if we already have the address on the interface */ static bool __attribute__((pure)) have_address(const void *addr_p, const interface_t *ifp, int family) { sin_addr_t *addr; const list_head_t *addr_l; if (!inet_inaddrcmp(family, addr_p, family == AF_INET ? (const void *)&ifp->sin_addr : (const void *)&ifp->sin6_addr)) return true; addr_l = family == AF_INET ? &ifp->sin_addr_l : &ifp->sin6_addr_l; list_for_each_entry(addr, addr_l, e_list) { if (!inet_inaddrcmp(family, addr_p, addr)) return true; } return false; } #endif /* * Netlink interface address lookup filter * We need to handle multiple primary address and * multiple secondary address to the same interface. * We also need to handle the same address on * multiple interfaces, for IPv6 link local addresses. */ static int netlink_if_address_filter(__attribute__((unused)) struct sockaddr_nl *snl, struct nlmsghdr *h) { struct ifaddrmsg *ifa; struct rtattr *tb[IFA_MAX + 1]; size_t len; union { void *addr; struct in_addr *in; struct in6_addr *in6; } addr; #ifdef _WITH_VRRP_ sin_addr_t *saddr; char addr_str[INET6_ADDRSTRLEN]; bool addr_chg = false; vrrp_t *vrrp; interface_t *ifp; ip_address_t *ipaddr; vrrp_t *address_vrrp; tracking_obj_t *top; bool is_tracking_saddr; #endif if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR) return 0; if (h->nlmsg_len < NLMSG_LENGTH(sizeof (struct ifaddrmsg))) return -1; ifa = NLMSG_DATA(h); /* Only IPv4 and IPv6 are valid for us */ if (ifa->ifa_family != AF_INET && ifa->ifa_family != AF_INET6) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifaddrmsg)); /* See -Wcast-align comment above, also applies to IFA_RTA */ parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), len); if (tb[IFA_LOCAL] == NULL) tb[IFA_LOCAL] = tb[IFA_ADDRESS]; if (tb[IFA_ADDRESS] == NULL) tb[IFA_ADDRESS] = tb[IFA_LOCAL]; /* Ignore tentative addresses - we can't do anything with them */ #if HAVE_DECL_IFA_FLAGS if ((tb[IFA_FLAGS] ? *(uint32_t *)RTA_DATA(tb[IFA_FLAGS]) : ifa->ifa_flags) & IFA_F_TENTATIVE) #else if (ifa->ifa_flags & IFA_F_TENTATIVE) #endif return 0; /* local interface address */ addr.addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL); if (addr.addr == NULL) return -1; #ifdef _WITH_VRRP_ #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_VRRP || __test_bit(CONFIG_TEST_BIT, &debug)) #endif { /* Fetch interface_t */ ifp = if_get_by_ifindex(ifa->ifa_index); if (!ifp) return 0; // ?? Only interested in link-local for IPv6 unless unicast // we take address from vrrp->ifp->base_ifp, unless we have made an IPv6 address // do we want to set a flag to say it is a generated link local address (or set saddr and track_saddr, but not saddr_from_config) // or can we just compare address to vrrp->ifp->base_ifp address. // We still need to consider non-vmac IPv6 if interface doesn't have a // link local address. if (h->nlmsg_type == RTM_NEWADDR) { if (!ignore_address_if_ours_or_link_local(ifa, addr.addr, ifp)) { /* The following code doesn't seem right. * 1. If using unicast, and the peers and not link local, then we need a non link-local address * 2. There is an argument that we should process this if vrrp->base_priority == VRRP_PRIO_OWNER * 3. We should ignore loopback addresses. * 4. If VRRP_PRIO_OWNER, should we check we have the VIP/eVIPs configured? * 5. In ignore_address...() is tracking_vrrp set if the VIP is no_track? */ /* If no address is set on interface then set the first time */ // TODO if saddr from config && track saddr, addresses must match if (ifa->ifa_family == AF_INET) { if (!ifp->sin_addr.s_addr) { ifp->sin_addr = *addr.in; if (!list_empty(&ifp->tracking_vrrp)) addr_chg = true; } else { /* Check we don't already have the address - * it might be being promoted from secondary to primary */ if (!have_address(addr.in, ifp, AF_INET)) if_extra_ipaddress_alloc(ifp, addr.in, AF_INET); } } else { if (IN6_IS_ADDR_UNSPECIFIED(&ifp->sin6_addr)) { ifp->sin6_addr = *addr.in6; if (!list_empty(&ifp->tracking_vrrp)) addr_chg = true; } #if defined _HAVE_VRRP_VMAC_ && !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE else if (ifp->is_ours && ifp->if_type == IF_TYPE_MACVLAN) { /* We already have an address; is this an auto generated link local address? * For some reason if we recreate the VMAC when the underlying interface is * recreated, deleting the autogenerated address doesn't get rid of the address */ remove_vmac_auto_gen_addr(ifp, addr.in6); } #endif else { /* Check we don't already have the address - * it might be being promoted from secondary to primary */ if (!have_address(addr.in6, ifp, AF_INET6)) if_extra_ipaddress_alloc(ifp, addr.in6, AF_INET6); } } if (addr_chg) { if (__test_bit(LOG_DETAIL_BIT, &debug)) { inet_ntop(ifa->ifa_family, addr.addr, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Assigned address %s for interface %s" , addr_str, ifp->ifname); } /* Now see if any vrrp instances were missing an interface address * and see if they can be brought up */ list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (__test_bit(VRRP_FLAG_TRACK_SADDR, &vrrp->flags) && vrrp->family == ifa->ifa_family) is_tracking_saddr = inaddr_equal(ifa->ifa_family, &vrrp->saddr, addr.addr); else is_tracking_saddr = false; if (vrrp->family == ifa->ifa_family && vrrp->saddr.ss_family == AF_UNSPEC && __test_bit(VRRP_FAULT_FL_NO_SOURCE_IP, &vrrp->flags_if_fault) && vrrp->ifp) { if (ifp == ( #ifdef _HAVE_VRRP_VMAC_ vrrp->family == AF_INET ? VRRP_CONFIGURED_IFP(vrrp) : #endif vrrp->ifp) && (!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags) || is_tracking_saddr)) { /* Copy the address */ if (ifa->ifa_family == AF_INET) inet_ip4tosockaddr(addr.in, &vrrp->saddr); else inet_ip6tosockaddr(addr.in6, &vrrp->saddr); try_up_instance(vrrp, false, VRRP_FAULT_FL_NO_SOURCE_IP); } #ifdef _HAVE_VRRP_VMAC_ /* If IPv6 link local and vmac doesn't have an address or we have * created one ourselves, add/replace the new address on the vmac */ else if (vrrp->family == AF_INET6 && IS_MAC_IP_VLAN(vrrp->ifp) && vrrp->ifp->is_ours && ifp == vrrp->ifp->base_ifp && !__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags)) { inet_ip6tosockaddr(addr.in6, &vrrp->saddr); if (!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags) || is_tracking_saddr) try_up_instance(vrrp, false, VRRP_FAULT_FL_NO_SOURCE_IP); else reset_link_local_address(&vrrp->ifp->sin6_addr, vrrp); } #endif } } } } } else { /* Mark the address as needing to go. We can't delete the address * until after down_instance is called, since it sends a prio 0 message */ if (ifa->ifa_family == AF_INET) { if (inaddr_equal(AF_INET, &ifp->sin_addr, addr.in)) { if (list_empty(&ifp->sin_addr_l)) addr_chg = true; else { saddr = list_last_entry(&ifp->sin_addr_l, sin_addr_t, e_list); ifp->sin_addr = saddr->u.sin_addr; if_extra_ipaddress_free(saddr); list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (VRRP_CONFIGURED_IFP(vrrp) != ifp) continue; if (vrrp->family != AF_INET || __test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags)) continue; inet_ip4tosockaddr(&ifp->sin_addr, &vrrp->saddr); } } } else { list_for_each_entry(saddr, &ifp->sin_addr_l, e_list) { if (inaddr_equal(AF_INET, &saddr->u.sin_addr, addr.in)) { if_extra_ipaddress_free(saddr); break; } } } } else if (ifa->ifa_scope == RT_SCOPE_LINK) { if (inaddr_equal(AF_INET6, &ifp->sin6_addr, addr.in6)) { if (list_empty(&ifp->sin6_addr_l)) addr_chg = true; else { saddr = list_last_entry(&ifp->sin6_addr_l, sin_addr_t, e_list); ifp->sin6_addr = saddr->u.sin6_addr; if_extra_ipaddress_free(saddr); list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (vrrp->ifp != ifp #ifdef _HAVE_VRRP_VMAC_ && ifp != vrrp->ifp->base_ifp #endif ) continue; if (vrrp->family != AF_INET6 || __test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags)) continue; inet_ip6tosockaddr(&ifp->sin6_addr, &vrrp->saddr); #ifdef _HAVE_VRRP_VMAC_ /* If vrrp->ifp is one of our VMACs, change its address */ if (ifp == vrrp->ifp->base_ifp && vrrp->ifp->is_ours) reset_link_local_address(addr.in6, vrrp); #endif } } } else { list_for_each_entry(saddr, &ifp->sin6_addr_l, e_list) { if (inaddr_equal(AF_INET6, &saddr->u.sin6_addr, addr.in6)) { if_extra_ipaddress_free(saddr); break; } } } } if (addr_chg && !list_empty(&ifp->tracking_vrrp)) { if (__test_bit(LOG_DETAIL_BIT, &debug)) { inet_ntop(ifa->ifa_family, addr.addr, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Deassigned address %s from interface %s" , addr_str, ifp->ifname); } if (ifa->ifa_family == AF_INET) ifp->sin_addr.s_addr = 0; else CLEAR_IP6_ADDR(&ifp->sin6_addr); /* See if any vrrp instances need to be downed */ list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (!vrrp->ifp) continue; if (ifp != vrrp->ifp #ifdef _HAVE_VRRP_VMAC_ && ifp != VRRP_CONFIGURED_IFP(vrrp) #endif ) continue; if (vrrp->family != ifa->ifa_family) continue; if (!inaddr_equal(ifa->ifa_family, vrrp->family == AF_INET ? &(PTR_CAST(struct sockaddr_in, &vrrp->saddr))->sin_addr : (void *)&(PTR_CAST(struct sockaddr_in6, &vrrp->saddr))->sin6_addr, addr.addr)) continue; is_tracking_saddr = __test_bit(VRRP_FLAG_TRACK_SADDR, &vrrp->flags) && vrrp->family == ifa->ifa_family && inaddr_equal(ifa->ifa_family, &vrrp->saddr, addr.addr); #ifdef _HAVE_VRRP_VMAC_ /* If we are a VMAC and took this address from the parent interface, we need to * release the address and create one for ourself */ if (ifa->ifa_family == AF_INET6 && __test_bit(VRRP_VMAC_BIT, &vrrp->flags) && ifp == vrrp->ifp->base_ifp && !__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) && !__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags)) { // This is rubbish if base i/f addr changed. Check against address generated from base i/f's MAC if (IF_ISUP(ifp) && replace_link_local_address(vrrp->ifp)) { addr_chg = false; inet_ip6tosockaddr(&vrrp->ifp->sin6_addr, &vrrp->saddr); } else if (IF_ISUP(ifp)) { /* We failed to add an address, so down the instance */ down_instance(vrrp, VRRP_FAULT_FL_NO_SOURCE_IP); vrrp->saddr.ss_family = AF_UNSPEC; } } else #endif if (ifp == ( #ifdef _HAVE_VRRP_VMAC_ vrrp->family == AF_INET ? VRRP_CONFIGURED_IFP(vrrp) : #endif vrrp->ifp) && vrrp->family == ifa->ifa_family && vrrp->saddr.ss_family != AF_UNSPEC && #ifdef _HAVE_VRRP_VMAC_ (vrrp->family != AF_INET6 || /* For an IPv6 VMAC if down removes the link local address */ !__test_bit(VRRP_VMAC_BIT, &vrrp->flags) || __test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) || IF_ISUP(ifp)) && #endif (!__test_bit(VRRP_FLAG_SADDR_FROM_CONFIG, &vrrp->flags) || is_tracking_saddr)) { /* Don't attempt to send an IPv6 advert if no address on the interface, but we can * for IPv4. So we want to clear the saddr before calling down_instance for IPv6, * but after for IPv4. */ if (vrrp->saddr.ss_family == AF_INET6 #ifdef _HAVE_VRRP_VMAC_ && !__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags) #endif ) vrrp->saddr.ss_family = AF_UNSPEC; down_instance(vrrp, VRRP_FAULT_FL_NO_SOURCE_IP); vrrp->saddr.ss_family = AF_UNSPEC; } } } if (addr_chg) { /* Now we can remove the address */ if (ifa->ifa_family == AF_INET) ifp->sin_addr.s_addr = 0; else CLEAR_IP6_ADDR(&ifp->sin6_addr); } } if (!addr_chg || list_empty(&ifp->tracking_vrrp)) { if (h->nlmsg_type == RTM_DELADDR) address_vrrp = address_is_ours(ifa, addr.addr, ifp); else address_vrrp = NULL; /* Display netlink operation */ if ( #ifdef _WITH_LVS_ __test_bit(LOG_ADDRESS_CHANGES, &debug) || #endif (__test_bit(LOG_DETAIL_BIT, &debug) && address_vrrp)) { inet_ntop(ifa->ifa_family, addr.addr, addr_str, sizeof(addr_str)); log_message(LOG_INFO, "Netlink reflector reports IP %s %s %s" , addr_str, h->nlmsg_type == RTM_NEWADDR ? "added to" : "removed from", ifp->ifname); } /* If one of our VIPs/eVIPs has been deleted, transition to backup */ if (address_vrrp && address_vrrp->state == VRRP_STATE_MAST) { set_vrrp_backup(address_vrrp); } } if (h->nlmsg_type == RTM_DELADDR) { /* Check if a static address has been deleted */ list_for_each_entry(ipaddr, &vrrp_data->static_addresses, e_list) { if (!ipaddr->dont_track && addr_is_equal(ifa, addr.addr, ipaddr, ifp)) { reinstate_static_address(ipaddr); break; } } } } #endif #ifdef _WITH_LVS_ #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_CHECKER) #endif { /* Refresh checkers state */ update_checker_activity(ifa->ifa_family, addr.addr, (h->nlmsg_type == RTM_NEWADDR)); } #endif return 0; } /* Our netlink parser */ static int netlink_parse_info(int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), nl_handle_t *nl, struct nlmsghdr *n, bool read_all) { ssize_t len; int ret = 0; int error; char *nlmsg_buf __attribute__((aligned(__alignof__(struct nlmsghdr)))) = NULL; int nlmsg_buf_size = 0; while (true) { struct iovec iov = { .iov_len = 0 }; struct sockaddr_nl snl; struct msghdr msg = { .msg_name = &snl, .msg_namelen = sizeof(snl), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0 }; struct nlmsghdr *h; /* Find out how big our receive buffer needs to be */ do { len = recvmsg(nl->fd, &msg, MSG_PEEK | MSG_TRUNC); } while (len < 0 && check_EINTR(errno)); if (len < 0) { ret = -1; break; } if (len == 0) break; if (len > nlmsg_buf_size) { FREE_PTR(nlmsg_buf); nlmsg_buf = MALLOC(len); nlmsg_buf_size = len; } iov.iov_base = nlmsg_buf; iov.iov_len = nlmsg_buf_size; do { len = recvmsg(nl->fd, &msg, 0); } while (len < 0 && check_EINTR(errno)); if (len < 0) { if (check_EAGAIN(errno)) break; if (errno == ENOBUFS) { log_message(LOG_INFO, "Netlink: Receive buffer overrun on %s socket - (%m)", nl == &nl_kernel ? "monitor" : "cmd"); log_message(LOG_INFO, " - increase the relevant netlink_rcv_bufs global parameter and/or set force"); } else log_message(LOG_INFO, "Netlink: recvmsg error on %s socket - %d (%m)", nl == &nl_kernel ? "monitor" : "cmd", errno); continue; } if (len == 0) { log_message(LOG_INFO, "Netlink: EOF"); ret = -1; break; } if (msg.msg_namelen != sizeof snl) { log_message(LOG_INFO, "Netlink: Sender address length error: length %u", msg.msg_namelen); ret = -1; break; } /* See -Wcast-align comment above, also applies to NLMSG_NEXT */ for (h = PTR_CAST(struct nlmsghdr, nlmsg_buf); NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) { /* Finish off reading. */ if (h->nlmsg_type == NLMSG_DONE) { FREE(nlmsg_buf); return ret; } /* Error handling. */ if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = PTR_CAST(struct nlmsgerr, NLMSG_DATA(h)); /* * If error == 0 then this is a netlink ACK. * return if not related to multipart message. */ if (err->error == 0) { if (!(h->nlmsg_flags & NLM_F_MULTI) && !read_all) { FREE(nlmsg_buf); return 0; } continue; } if (h->nlmsg_len < NLMSG_LENGTH(sizeof (struct nlmsgerr))) { log_message(LOG_INFO, "Netlink: error: message truncated"); FREE(nlmsg_buf); return -1; } if (n && (err->error == -EEXIST) && ((n->nlmsg_type == RTM_NEWROUTE) || (n->nlmsg_type == RTM_NEWADDR))) { FREE(nlmsg_buf); return 0; } /* If have more than one IPv4 address in the same CIDR * and the "primary" address is removed, unless promote_secondaries * is configured on the interface, all the "secondary" addresses * in the same CIDR are deleted */ if (n && err->error == -EADDRNOTAVAIL && n->nlmsg_type == RTM_DELADDR) { if (!(h->nlmsg_flags & NLM_F_MULTI)) { FREE(nlmsg_buf); return 0; } continue; } #ifdef _WITH_VRRP_ if (netlink_error_ignore != -err->error) #endif log_message(LOG_INFO, "Netlink: error: %s(%d), type=%s(%u), seq=%u, pid=%u", strerror(-err->error), -err->error, get_nl_msg_type(err->msg.nlmsg_type), err->msg.nlmsg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid); FREE(nlmsg_buf); return -1; } #ifdef _WITH_VRRP_ /* Skip messages on the kernel reflection channel * caused by commands from our cmd channel */ if ( #ifndef _ONE_PROCESS_DEBUG_ prog_type == PROG_TYPE_VRRP && #endif h->nlmsg_type != RTM_NEWLINK && h->nlmsg_type != RTM_DELLINK && h->nlmsg_type != RTM_NEWROUTE && // Allow NEWADDR/DELADDR for ipvlans nl != &nl_cmd && h->nlmsg_pid == nl_cmd.nl_pid) continue; #endif error = (*filter) (&snl, h); if (error < 0) { log_message(LOG_INFO, "Netlink: filter function error"); ret = error; } if (!(h->nlmsg_flags & NLM_F_MULTI) && !read_all) { FREE(nlmsg_buf); return ret; } } /* After error care. */ if (msg.msg_flags & MSG_TRUNC) { log_message(LOG_INFO, "Netlink: error: message truncated"); continue; } if (len) { log_message(LOG_INFO, "Netlink: error: data remnant size %zd", len); ret = -1; break; } } if (nlmsg_buf) FREE(nlmsg_buf); return ret; } #ifdef _WITH_VRRP_ /* Out talk filter */ static int netlink_talk_filter(__attribute__((unused)) struct sockaddr_nl *snl, struct nlmsghdr *h) { log_message(LOG_INFO, "Netlink: ignoring message type 0x%04x", h->nlmsg_type); return 0; } /* send message to netlink kernel socket, then receive response */ ssize_t netlink_talk(nl_handle_t *nl, struct nlmsghdr *n) { ssize_t status; struct sockaddr_nl snl; struct iovec iov = { .iov_base = n, .iov_len = n->nlmsg_len }; struct msghdr msg = { .msg_name = &snl, .msg_namelen = sizeof(snl), .msg_iov = &iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0 }; memset(&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; n->nlmsg_seq = ++nl->seq; /* Request Netlink acknowledgement */ n->nlmsg_flags |= NLM_F_ACK; #ifdef _NETLINK_TIMERS_ gettimeofday(&start_time, NULL); #endif /* Send message to netlink interface. */ status = sendmsg(nl->fd, &msg, 0); if (status < 0) { log_message(LOG_INFO, "Netlink: sendmsg(%d) cmd %d error: %s", nl->fd, n->nlmsg_type, strerror(errno)); return -1; } status = netlink_parse_info(netlink_talk_filter, nl, n, false); #ifdef _NETLINK_TIMERS_ /* Special case for NEWLINK - treat create separately; it is also used to up an interface etc. */ int index = n->nlmsg_type == RTM_NEWLINK && (n->nlmsg_flags & NLM_F_CREATE) ? 0 : n->nlmsg_type; gettimeofday(&end_time, NULL); if (index <= MAX_NETLINK_TIMER) { netlink_times[index].tv_sec += end_time.tv_sec - start_time.tv_sec; netlink_times[index].tv_usec += end_time.tv_usec - start_time.tv_usec; netlink_count[index]++; if (netlink_times[index].tv_usec < 0) netlink_times[index].tv_usec += 1000000, netlink_times[index].tv_sec--; else if (netlink_times[index].tv_usec > 1000000) netlink_times[index].tv_usec -= 1000000, netlink_times[index].tv_sec++; } #endif return status; } #endif /* Fetch a specific type of information from netlink kernel */ static int netlink_request(nl_handle_t *nl, unsigned char family, uint16_t type, #ifndef _WITH_VRRP_ __attribute__((unused)) #endif char *name) { ssize_t status; struct sockaddr_nl snl = { .nl_family = AF_NETLINK }; struct { struct nlmsghdr nlh; struct ifinfomsg i; char buf[64]; } req = { .nlh.nlmsg_type = type }; req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof req.i); req.nlh.nlmsg_flags = NLM_F_REQUEST; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = ++nl->seq; req.i.ifi_family = family; #ifdef _WITH_VRRP_ if (name) addattr_l(&req.nlh, sizeof req, IFLA_IFNAME, name, strlen(name) + 1); else #endif req.nlh.nlmsg_flags |= NLM_F_DUMP; #if HAVE_DECL_RTEXT_FILTER_SKIP_STATS /* The following produces a -Wstringop-overflow warning due to writing * 4 bytes into a region of size 0. This is, however, safe. * By GCC 14 the warning is -Warray-bounds= */ RELAX_ARRAY_BOUNDS_START addattr32(&req.nlh, sizeof req, IFLA_EXT_MASK, RTEXT_FILTER_SKIP_STATS); RELAX_ARRAY_BOUNDS_END #endif status = sendto(nl->fd, (void *) &req, sizeof (req) , 0, PTR_CAST(struct sockaddr, &snl), sizeof (snl)); if (status < 0) { log_message(LOG_INFO, "Netlink: sendto() failed: %s", strerror(errno)); return -1; } return 0; } #ifdef _WITH_VRRP_ void process_if_status_change(interface_t *ifp) { vrrp_t *vrrp; tracking_obj_t *top; bool now_up = FLAGS_UP(ifp->ifi_flags); if (now_up) ifp->seen_up = true; /* The state of the interface has changed from up to down or vice versa. * Find which vrrp instances are affected */ list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (top->weight == VRRP_NOT_TRACK_IF) { /* We might want to restore things to the interface if it is coming up */ continue; } if (top->weight) { if (now_up) vrrp->total_priority += abs(top->weight) * top->weight_multiplier; else vrrp->total_priority -= abs(top->weight) * top->weight_multiplier; vrrp_set_effective_priority(vrrp); continue; } /* This vrrp's interface or underlying interface has changed */ if (now_up == (top->weight_multiplier == 1)) { #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && VRRP_CONFIGURED_IFP(vrrp) == ifp) try_up_instance(vrrp, false, VRRP_FAULT_FL_BASE_INTERFACE_DOWN); else #endif { /* assuming there is only one tracked interface per vrrp : to be checked */ try_up_instance(vrrp, false, VRRP_FAULT_FL_INTERFACE_DOWN); } } else { #ifdef _HAVE_VRRP_VMAC_ if (__test_bit(VRRP_VMAC_BIT, &vrrp->flags) && VRRP_CONFIGURED_IFP(vrrp) == ifp) down_instance(vrrp, VRRP_FAULT_FL_BASE_INTERFACE_DOWN); else #endif down_instance(vrrp, VRRP_FAULT_FL_INTERFACE_DOWN); } } } static void process_interface_flags_change(interface_t *ifp, unsigned ifi_flags) { bool now_up = FLAGS_UP(ifi_flags); #ifdef _HAVE_VRRP_VMAC_ tracking_obj_t *top; vrrp_t *vrrp; #endif ifp->ifi_flags = ifi_flags; if (!list_empty(&ifp->tracking_vrrp)) { log_message(LOG_INFO, "Netlink reports %s %s", ifp->ifname, now_up ? "up" : "down"); #ifdef _HAVE_VRRP_VMAC_ if (ifp->vmac_type && ifp->is_ours && ifp->hw_addr[sizeof(ll_addr) - 2] == 0x02 && /* Is it an IPv6 vmac? */ now_up && IN6_IS_ADDR_UNSPECIFIED(&ifp->sin6_addr)) { list_for_each_entry(top, &ifp->tracking_vrrp, e_list) { vrrp = top->obj.vrrp; if (!__test_bit(VRRP_VMAC_XMITBASE_BIT, &vrrp->flags)) { set_link_local_address(vrrp); break; } } } #endif process_if_status_change(ifp); } if (!now_up) interface_down(ifp); else interface_up(ifp); } static void delayed_if_flags_change_thread(thread_ref_t thread) { /* what if interface has been deleted, name changed, etc? */ interface_t *ifp = PTR_CAST(interface_t, thread->arg); ifp->flags_change_thread = NULL; process_interface_flags_change(ifp, thread->u.val); } static void update_interface_flags(interface_t *ifp, unsigned ifi_flags, bool immediate) { bool was_up, now_up; unsigned debounce_timer; if (ifi_flags == ifp->ifi_flags || immediate) { if (ifp->flags_change_thread) { thread_cancel(ifp->flags_change_thread); ifp->flags_change_thread = NULL; } if (!immediate) return; } if (!vrrp_data) return; /* We get called after a VMAC is created, but before tracking_vrrp is set */ /* For an interface to be really up, any underlying interface must also be up */ was_up = IF_FLAGS_UP(ifp); now_up = FLAGS_UP(ifi_flags); if (ifp->flags_change_thread) { if (ifi_flags == ifp->flags_change_thread->u.uval) return; /* If up/down status is same as last pending status change, * just update the thread's version of the interface state */ if (FLAGS_UP(ifp->flags_change_thread->u.uval) == now_up) { thread_arg2 u = { .uval = ifi_flags }; thread_update_arg2(ifp->flags_change_thread, &u); return ; } thread_cancel(ifp->flags_change_thread); ifp->flags_change_thread = NULL; log_message(LOG_INFO, "Delay timer for %s cancelled", ifp->ifname); } if (was_up == now_up) return; debounce_timer = now_up ? ifp->up_debounce_timer : ifp->down_debounce_timer; if (ifp->seen_up && debounce_timer && !immediate) { ifp->flags_change_thread = thread_add_timer_uval(master, delayed_if_flags_change_thread, ifp, ifi_flags, debounce_timer); log_message(LOG_INFO, "%s: Adding flags change from 0x%x to 0x%x delay by %u", ifp->ifname, ifp->ifi_flags, ifi_flags, debounce_timer); return; } if (now_up) ifp->seen_up = true; process_interface_flags_change(ifp, ifi_flags); } static const char * get_mac_string(int type) { switch (type) { case IFLA_BROADCAST: return "Broadcast"; case IFLA_ADDRESS: return "Address"; default: return "Unknown Type"; } } static bool netlink_if_get_ll_addr(interface_t *ifp, struct rtattr *tb[], int type, char *name) { size_t i; if (tb[type]) { size_t hw_addr_len = RTA_PAYLOAD(tb[type]); if (hw_addr_len > sizeof(ifp->hw_addr)) { log_message(LOG_ERR, " %s MAC address for %s is too large: %zu", get_mac_string(type), name, hw_addr_len); return false; } switch (type) { case IFLA_ADDRESS: ifp->hw_addr_len = hw_addr_len; memcpy(ifp->hw_addr, RTA_DATA(tb[type]), hw_addr_len); /* * Don't allow a hardware address of all zeroes * Mark hw_addr_len as 0 to warn */ for (i = 0; i < hw_addr_len; i++) if (ifp->hw_addr[i] != 0) break; if (i == hw_addr_len) ifp->hw_addr_len = 0; else ifp->hw_addr_len = hw_addr_len; break; case IFLA_BROADCAST: memcpy(ifp->hw_addr_bcast, RTA_DATA(tb[type]), hw_addr_len); break; default: return false; } } return true; } #ifdef _HAVE_IPV4_DEVCONF_ static void parse_af_spec(struct rtattr* attr, interface_t *ifp) { struct rtattr* afspec[AF_INET6 + 1]; struct rtattr* inet[IFLA_INET_MAX + 1]; uint32_t* inet_devconf; if (!attr) return; parse_rtattr_nested(afspec, AF_INET6, attr); if (afspec[AF_INET]) { parse_rtattr_nested(inet, IFLA_INET_MAX, afspec[AF_INET]); if (inet[IFLA_INET_CONF]) { inet_devconf = RTA_DATA(inet[IFLA_INET_CONF]); #ifdef _HAVE_VRRP_VMAC_ ifp->arp_ignore = inet_devconf[IPV4_DEVCONF_ARP_IGNORE - 1]; ifp->arp_filter = inet_devconf[IPV4_DEVCONF_ARPFILTER - 1]; if (ifp->rp_filter == UINT_MAX) ifp->rp_filter = inet_devconf[IPV4_DEVCONF_RP_FILTER - 1]; #endif ifp->promote_secondaries = inet_devconf[IPV4_DEVCONF_PROMOTE_SECONDARIES - 1]; } } } #endif static bool netlink_if_link_populate(interface_t *ifp, struct rtattr *tb[], struct ifinfomsg *ifi) { char *name; #ifdef _HAVE_VRRP_VMAC_ struct rtattr* linkinfo[IFLA_INFO_MAX+1]; #if defined _HAVE_VRRP_IPVLAN_ && defined _HAVE_VRF_ struct rtattr* linkattr[max(max(IFLA_MACVLAN_MAX, IFLA_IPVLAN_MAX), IFLA_VRF_MAX) + 1]; #elif defined _HAVE_VRRP_IPVLAN_ struct rtattr* linkattr[max(IFLA_MACVLAN_MAX, IFLA_IPVLAN_MAX) + 1]; #elif defined _HAVE_VRF_ struct rtattr* linkattr[max(IFLA_MACVLAN_MAX, IFLA_VRF_MAX) + 1]; #else struct rtattr* linkattr[IFLA_MACVLAN_MAX + 1]; #endif bool was_vlan; #ifdef _HAVE_VRF_ struct rtattr *vrf_attr[IFLA_VRF_MAX + 1]; bool is_vrf = false; uint32_t new_vrf_master_index; bool is_vrf_master = false; #endif #endif #ifdef _HAVE_VRRP_VMAC_ was_vlan = IS_MAC_IP_VLAN(ifp); #endif name = (char *)RTA_DATA(tb[IFLA_IFNAME]); /* Fill the interface structure */ strcpy_safe(ifp->ifname, name); ifp->ifindex = (ifindex_t)ifi->ifi_index; #ifdef _HAVE_VRRP_VMAC_ ifp->if_type = IF_TYPE_STANDARD; #endif #ifdef HAVE_IFLA_LINK_NETNSID /* from Linux v4.0 */ ifp->base_netns_id = -1; #endif #ifdef _HAVE_VRRP_VMAC_ if (tb[IFLA_LINKINFO]) { parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]); if (linkinfo[IFLA_INFO_KIND]) { if (!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "macvlan") || !strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "macvtap")) { ifp->if_type = IF_TYPE_MACVLAN; parse_rtattr_nested(linkattr, IFLA_MACVLAN_MAX, linkinfo[IFLA_INFO_DATA]); } #ifdef _HAVE_VRRP_IPVLAN_ else if (!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "ipvlan") || !strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "ipvtap")) { ifp->if_type = IF_TYPE_IPVLAN; parse_rtattr_nested(linkattr, IFLA_IPVLAN_MAX, linkinfo[IFLA_INFO_DATA]); } #endif #ifdef _HAVE_VRF_ else if (!strcmp((char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vrf") ) { is_vrf = true; ifp->if_type = IF_TYPE_VRF; parse_rtattr_nested(vrf_attr, IFLA_VRF_MAX, linkinfo[IFLA_INFO_DATA]); if (vrf_attr[IFLA_VRF_TABLE]) ifp->vrf_tb_id = *PTR_CAST(uint32_t, RTA_DATA(vrf_attr[IFLA_VRF_TABLE])); } #endif } } #ifdef _HAVE_IPV4_DEVCONF_ if (tb[IFLA_AF_SPEC]) parse_af_spec(tb[IFLA_AF_SPEC], ifp); #endif /* Check there hasn't been an unsupported interface type change */ if (!global_data->allow_if_changes && ifp->seen_interface) { /* If it was a macvlan and now isn't, or vice versa, * then the interface type has changed */ if (IS_MAC_IP_VLAN(ifp) != was_vlan) return false; /* If a macvlan, check the underlying interface hasn't changed */ if (IS_MAC_IP_VLAN(ifp) && (!tb[IFLA_LINK] || ifp->base_ifp->ifindex != *PTR_CAST(uint32_t, RTA_DATA(tb[IFLA_LINK])))) return false; } #endif ifp->mtu = *PTR_CAST(uint32_t, RTA_DATA(tb[IFLA_MTU])); ifp->hw_type = ifi->ifi_type; if (!netlink_if_get_ll_addr(ifp, tb, IFLA_ADDRESS, name)) return false; if (!netlink_if_get_ll_addr(ifp, tb, IFLA_BROADCAST, name)) return false; #ifdef _HAVE_VRRP_VMAC_ ifp->base_ifp = ifp; ifp->base_ifindex = 0; ifp->group = *PTR_CAST(uint32_t, RTA_DATA(tb[IFLA_GROUP])); if (tb[IFLA_LINKINFO]) { if (linkinfo[IFLA_INFO_KIND]) { /* See if this interface is a MACVLAN */ if (IS_MAC_IP_VLAN(ifp)) { if (((ifp->if_type == IF_TYPE_MACVLAN && linkattr[IFLA_MACVLAN_MODE]) #ifdef _HAVE_VRRP_IPVLAN_ || (ifp->if_type == IF_TYPE_IPVLAN && linkattr[IFLA_IPVLAN_MODE]) #endif ) && tb[IFLA_LINK]) { if (ifp->if_type == IF_TYPE_MACVLAN) ifp->vmac_type = *PTR_CAST(uint32_t, RTA_DATA(linkattr[IFLA_MACVLAN_MODE])); #ifdef _HAVE_VRRP_IPVLAN_ else { ifp->vmac_type = *PTR_CAST(uint32_t, RTA_DATA(linkattr[IFLA_IPVLAN_MODE])); #if HAVE_DECL_IFLA_IPVLAN_FLAGS ifp->ipvlan_flags = *PTR_CAST(uint32_t, RTA_DATA(linkattr[IFLA_IPVLAN_FLAGS])); #endif } #endif ifp->base_ifindex = *PTR_CAST(uint32_t, RTA_DATA(tb[IFLA_LINK])); #ifdef HAVE_IFLA_LINK_NETNSID /* from Linux v4.0 */ if (tb[IFLA_LINK_NETNSID]) /* Only use link details if in same network namespace */ ifp->base_netns_id = *PTR_CAST(int32_t, RTA_DATA(tb[IFLA_LINK_NETNSID])); else #endif { ifp->base_ifp = if_get_by_ifindex(ifp->base_ifindex); if (ifp->base_ifp) ifp->base_ifindex = 0; /* Make sure this isn't used at runtime */ else ifp->base_ifp = ifp; } } } #ifdef _HAVE_VRF_ else if (is_vrf) { if (vrf_attr[IFLA_VRF_TABLE]) { ifp->vrf_master_ifp = ifp; is_vrf_master = true; } } #endif #ifdef _FIXED_IF_TYPE_ if (strcmp(_FIXED_IF_TYPE_, (char *)RTA_DATA(linkinfo[IFLA_INFO_KIND]))) #endif ifp->changeable_type = true; } } #ifdef _HAVE_VRF_ /* If we don't have the master interface details yet, we won't know * if the master is a VRF master, but we sort that out later */ if (!is_vrf_master) { if (tb[IFLA_MASTER]) { new_vrf_master_index = *PTR_CAST(uint32_t, RTA_DATA(tb[IFLA_MASTER])); if (!ifp->vrf_master_ifp || new_vrf_master_index != ifp->vrf_master_ifp->ifindex) { ifp->vrf_master_ifindex = new_vrf_master_index; ifp->vrf_master_ifp = if_get_by_ifindex(ifp->vrf_master_ifindex); if (ifp->vrf_master_ifp) { if (ifp->vrf_master_ifp->vrf_master_ifp != ifp->vrf_master_ifp) ifp->vrf_master_ifp = NULL; ifp->vrf_master_ifindex = 0; /* Make sure this isn't used at runtime */ update_vmac_vrfs(ifp); } } } else { ifp->vrf_master_ifindex = 0; if (ifp->vrf_master_ifp) { ifp->vrf_master_ifp = NULL; update_vmac_vrfs(ifp); } } } #endif ifp->rp_filter = UINT_MAX; /* We have not read it yet */ #endif ifp->ifi_flags = ifi->ifi_flags; if (FLAGS_UP(ifi->ifi_flags)) ifp->seen_up = true; return true; } /* Netlink interface link lookup filter */ static int netlink_if_link_filter(__attribute__((unused)) struct sockaddr_nl *snl, struct nlmsghdr *h) { struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; interface_t *ifp; size_t len; char *name; ifi = NLMSG_DATA(h); if (h->nlmsg_type != RTM_NEWLINK) return 0; if (h->nlmsg_len < NLMSG_LENGTH(sizeof (struct ifinfomsg))) return -1; len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg)); /* See -Wcast-align comment above, also applies to IFLA_RTA */ /* Interface name lookup */ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); if (tb[IFLA_IFNAME] == NULL) return -1; name = (char *)RTA_DATA(tb[IFLA_IFNAME]); /* Skip it if already exists */ ifp = if_get_by_ifname(name, IF_CREATE_NETLINK); /* Fill the interface structure */ if (!netlink_if_link_populate(ifp, tb, ifi)) return -1; if (ifp->ifindex) update_interface_flags(ifp, ifi->ifi_flags, true); return 0; } /* Interfaces lookup bootstrap function */ int netlink_interface_lookup(char *name) { /* Interface lookup */ if (netlink_request(&nl_cmd, AF_PACKET, RTM_GETLINK, name) < 0) return -1; return netlink_parse_info(netlink_if_link_filter, &nl_cmd, NULL, false); } #endif /* Addresses lookup bootstrap function */ static int netlink_address_lookup(void) { int status; /* IPv4 Address lookup */ if (netlink_request(&nl_cmd, AF_INET, RTM_GETADDR, NULL) < 0) return -1; if ((status = netlink_parse_info(netlink_if_address_filter, &nl_cmd, NULL, false))) return status; /* IPv6 Address lookup */ if (netlink_request(&nl_cmd, AF_INET6, RTM_GETADDR, NULL) < 0) return -1; return netlink_parse_info(netlink_if_address_filter, &nl_cmd, NULL, false); } #ifdef _WITH_VRRP_ /* Netlink flag Link update */ static int netlink_link_filter(__attribute__((unused)) struct sockaddr_nl *snl, struct nlmsghdr *h) { struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; interface_t *ifp; size_t len; char *name; #ifdef _HAVE_VRF_ uint32_t new_master_index; interface_t *new_master_ifp; #endif uint32_t old_mtu; size_t hw_addr_len; char mac_buf[3 * sizeof(ifp->hw_addr)]; char old_mac_buf[3 * sizeof(ifp->hw_addr)]; list_head_t sav_tracking_vrrp; list_head_t sav_e_list; garp_delay_t *sav_garp_delay; bool immediate_change = false; if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) return 0; if (h->nlmsg_len < NLMSG_LENGTH(sizeof (struct ifinfomsg))) return -1; len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifinfomsg)); /* Interface name lookup */ ifi = NLMSG_DATA(h); /* See -Wcast-align comment above, also applies to IFLA_RTA */ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); if (tb[IFLA_IFNAME] == NULL) return -1; name = (char *)RTA_DATA(tb[IFLA_IFNAME]); /* Ignore NEWLINK messages with ifi_change == 0 and IFLA_WIRELESS set See for example https://bugs.chromium.org/p/chromium/issues/detail?id=501982 */ if (!ifi->ifi_change && tb[IFLA_WIRELESS] && h->nlmsg_type == RTM_NEWLINK) return 0; /* find the interface_t. If the interface doesn't exist in the interface * list and this is a new interface add it to the interface list. * If an interface with the same name exists overwrite the older * structure and fill it with the new interface information. */ ifp = if_get_by_ifindex((ifindex_t)ifi->ifi_index); if (ifp) { if (h->nlmsg_type == RTM_DELLINK) { if ((!list_empty(&ifp->tracking_vrrp)) || __test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Interface %s deleted", ifp->ifname); #ifndef _ONE_PROCESS_DEBUG_ if (prog_type != PROG_TYPE_VRRP) { ifp->ifi_flags = 0; ifp->ifindex = 0; } else #endif cleanup_lost_interface(ifp); ifp->seen_up = false; #ifdef _HAVE_VRRP_VMAC_ /* If this was a vmac we created, create it again, so long as the underlying i/f exists */ if (ifp->is_ours && !ifp->deleting #ifndef _ONE_PROCESS_DEBUG_ && prog_type == PROG_TYPE_VRRP #endif ) thread_add_event(master, recreate_vmac_thread, ifp, 0); #endif } else { if (tb[IFLA_ADDRESS]) { hw_addr_len = RTA_PAYLOAD(tb[IFLA_ADDRESS]); if (ifp->hw_addr_len != hw_addr_len || memcmp(ifp->hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), hw_addr_len)) { if (hw_addr_len > sizeof(ifp->hw_addr)) { log_message(LOG_ERR, "MAC %s for %s is too large: %zu", get_mac_string(IFLA_ADDRESS), ifp->ifname, hw_addr_len); } else { if (__test_bit(LOG_DETAIL_BIT, &debug)) { if (!ifp->hw_addr_len) strcpy(old_mac_buf, "none"); else format_mac_buf(old_mac_buf, sizeof old_mac_buf, ifp->hw_addr, ifp->hw_addr_len); } ifp->hw_addr_len = hw_addr_len; memcpy(ifp->hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), hw_addr_len); if (__test_bit(LOG_DETAIL_BIT, &debug)) { format_mac_buf(mac_buf, sizeof mac_buf, ifp->hw_addr, ifp->hw_addr_len); log_message(LOG_INFO, "(%s) MAC %s changed from %s to %s", ifp->ifname, get_mac_string(IFLA_ADDRESS), old_mac_buf, mac_buf); } } } } if (tb[IFLA_BROADCAST]) { hw_addr_len = RTA_PAYLOAD(tb[IFLA_BROADCAST]); if (ifp->hw_addr_len && ifp->hw_addr_len != hw_addr_len) log_message(LOG_ERR, "MAC broadcast address length %zu does not match MAC address length %zu", hw_addr_len, ifp->hw_addr_len); else if(memcmp(ifp->hw_addr_bcast, RTA_DATA(tb[IFLA_BROADCAST]), hw_addr_len)) { if (hw_addr_len > sizeof(ifp->hw_addr_bcast)) { log_message(LOG_ERR, "MAC %s for %s is too large: %zu", get_mac_string(IFLA_BROADCAST), ifp->ifname, hw_addr_len); } else { if (__test_bit(LOG_DETAIL_BIT, &debug)) format_mac_buf(old_mac_buf, sizeof old_mac_buf, ifp->hw_addr_bcast, ifp->hw_addr_len); ifp->hw_addr_len = hw_addr_len; memcpy(ifp->hw_addr_bcast, RTA_DATA(tb[IFLA_BROADCAST]), hw_addr_len); if (__test_bit(LOG_DETAIL_BIT, &debug)) { format_mac_buf(mac_buf, sizeof mac_buf, ifp->hw_addr_bcast, ifp->hw_addr_len); log_message(LOG_INFO, "(%s) MAC %s changed from %s to %s", ifp->ifname, get_mac_string(IFLA_BROADCAST), old_mac_buf, mac_buf); } } } } #ifdef _HAVE_VRRP_VMAC_ if (tb[IFLA_GROUP]) ifp->group = *PTR_CAST(uint32_t, RTA_DATA(tb[IFLA_GROUP])); #endif if (strcmp(ifp->ifname, name)) { /* The name can change, so handle that here */ log_message(LOG_INFO, "Interface name has changed from %s to %s", ifp->ifname, name); #ifndef _ONE_PROCESS_DEBUG_ if (prog_type != PROG_TYPE_VRRP) { ifp->ifi_flags = 0; ifp->ifindex = 0; } else #endif cleanup_lost_interface(ifp); #ifdef _HAVE_VRRP_VMAC_ /* If this was one of our vmacs, create it again */ if (ifp->is_ours #ifndef _ONE_PROCESS_DEBUG_ && prog_type == PROG_TYPE_VRRP #endif ) { /* Change the mac address on the interface, so we can create a new vmac */ /* Now create our VMAC again */ if (ifp->base_ifp->ifindex) thread_add_event(master, recreate_vmac_thread, ifp, 0); } else #endif ifp = NULL; /* Set ifp to null, to force creating a new interface_t */ immediate_change = true; } else if (ifp->ifindex) { #ifdef _HAVE_VRF_ /* Now check if the VRF info is changed */ if (tb[IFLA_MASTER]) { new_master_index = *PTR_CAST(uint32_t, RTA_DATA(tb[IFLA_MASTER])); new_master_ifp = if_get_by_ifindex(new_master_index); } else new_master_ifp = NULL; if (new_master_ifp != ifp->vrf_master_ifp) { ifp->vrf_master_ifp = new_master_ifp; update_vmac_vrfs(ifp); } #endif /* Check if the MTU has increased */ if ( #ifndef _ONE_PROCESS_DEBUG_ prog_type == PROG_TYPE_VRRP && #endif tb[IFLA_MTU]) { old_mtu = ifp->mtu; ifp->mtu = *PTR_CAST(uint32_t, RTA_DATA(tb[IFLA_MTU])); if (old_mtu != ifp->mtu && !list_empty(&ifp->tracking_vrrp)) update_mtu(ifp); } #ifdef _HAVE_IPV4_DEVCONF_ if (tb[IFLA_AF_SPEC]) parse_af_spec(tb[IFLA_AF_SPEC], ifp); #endif #ifdef _WITH_LINKBEAT_ /* Ignore interface if we are using linkbeat on it */ if (ifp->linkbeat_use_polling) return 0; #endif } else ifp = NULL; } } if (!ifp) { if (h->nlmsg_type == RTM_NEWLINK) { ifp = if_get_by_ifname(name, IF_CREATE_NETLINK); /* Since the garp_delay and tracking_vrrp are set up by name, * it is reasonable to preserve them. * If what is created is a vmac, we could end up in a complete mess. */ sav_garp_delay = ifp->garp_delay; list_copy(&sav_tracking_vrrp, &ifp->tracking_vrrp); old_mtu = ifp->mtu; if_extra_ipaddress_free_list(&ifp->sin_addr_l); if_extra_ipaddress_free_list(&ifp->sin6_addr_l); /* Save the list_head entry itself */ sav_e_list = ifp->e_list; memset(ifp, 0, sizeof(interface_t)); /* Restore the list_head entry */ ifp->e_list = sav_e_list; /* Re-establish lists */ INIT_LIST_HEAD(&ifp->sin_addr_l); INIT_LIST_HEAD(&ifp->sin6_addr_l); list_copy(&ifp->tracking_vrrp, &sav_tracking_vrrp); ifp->garp_delay = sav_garp_delay; if (!netlink_if_link_populate(ifp, tb, ifi)) return -1; if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Interface %s added", ifp->ifname); update_added_interface(ifp); #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_VRRP) #endif if (ifp->mtu > old_mtu) alloc_vrrp_buffer(ifp->mtu); /* We need to see a transition to up, so mark it down for now */ ifp->ifi_flags &= ~(IFF_UP | IFF_RUNNING); } else { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Unknown interface %s deleted", (char *)tb[IFLA_IFNAME]); return 0; } } /* Update flags. Flags == 0 means interface deleted. */ update_interface_flags(ifp, (h->nlmsg_type == RTM_DELLINK) ? 0 : ifi->ifi_flags, immediate_change); return 0; } #ifdef _WITH_VRRP_ static int netlink_route_filter(__attribute__((unused)) struct sockaddr_nl *snl, struct nlmsghdr *h) { struct rtmsg *rt; struct rtattr *tb[RTA_MAX + 1]; size_t len; vrrp_t *vrrp; ip_route_t *route; if (h->nlmsg_type != RTM_NEWROUTE && h->nlmsg_type != RTM_DELROUTE) return 0; if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*rt))) return -1; rt = NLMSG_DATA(h); if (rt->rtm_protocol != RTPROT_KEEPALIVED) { /* It is not a route we are monitoring - ignore it */ return 0; } /* Only IPv4 and IPv6 are valid for us */ if (rt->rtm_family != AF_INET && rt->rtm_family != AF_INET6) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct rtmsg)); /* See -Wcast-align comment above, also applies to RTM_RTA */ parse_rtattr(tb, RTA_MAX, RTM_RTA(rt), len); if (!(route = route_is_ours(rt, tb, &vrrp))) return 0; route->set = (h->nlmsg_type == RTM_NEWROUTE); /* Matching route */ if (h->nlmsg_type == RTM_NEWROUTE) { /* If we haven't specified a dev for the route, save the link the route * has been added to. */ if (tb[RTA_OIF]) { route->configured_ifindex = *PTR_CAST(uint32_t, RTA_DATA(tb[RTA_OIF])); if (route->oif && route->oif->ifindex != route->configured_ifindex) log_message(LOG_INFO, "route added index %" PRIu32 " != config index %u", route->configured_ifindex, route->oif->ifindex); } else route->configured_ifindex = 0; return 0; } /* We are only interested in route deletions now */ if (route->dont_track) return 0; if (vrrp) { if (vrrp->state != VRRP_STATE_MAST) return 0; set_vrrp_backup(vrrp); } else reinstate_static_route(route); return 0; } static int netlink_rule_filter(__attribute__((unused)) struct sockaddr_nl *snl, struct nlmsghdr *h) { struct fib_rule_hdr *frh; struct rtattr *tb[FRA_MAX + 1]; size_t len; vrrp_t *vrrp; ip_rule_t *ip_rule; if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) return 0; if (h->nlmsg_len < NLMSG_LENGTH(sizeof(*frh))) return -1; frh = NLMSG_DATA(h); /* Only IPv4 and IPv6 are valid for us */ if (frh->family != AF_INET && frh->family != AF_INET6) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct rtmsg)); /* See -Wcast-align comment above, also applies to RTM_RTA */ parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); #if HAVE_DECL_FRA_PROTOCOL if (tb[FRA_PROTOCOL] && *PTR_CAST(uint8_t, RTA_DATA(tb[FRA_PROTOCOL])) != RTPROT_KEEPALIVED) { /* It is not a rule we are monitoring - ignore it */ return 0; } #endif /* We are only interested in rule deletions now */ if (h->nlmsg_type != RTM_DELRULE) return 0; if (!(ip_rule = rule_is_ours(frh, tb, &vrrp))) return 0; ip_rule->set = false; if (ip_rule->dont_track) return 0; if (vrrp) set_vrrp_backup(vrrp); else reinstate_static_rule(ip_rule); return 0; } #endif #endif /* Netlink kernel message reflection */ static int netlink_broadcast_filter(struct sockaddr_nl *snl, struct nlmsghdr *h) { switch (h->nlmsg_type) { case RTM_NEWLINK: case RTM_DELLINK: /* It appears that older kernels (certainly 2.6.32) can * send RTM_NEWLINK (but not RTM_DELLINK) messages even * when RTNLGRP_LINK has not been subscribed to. This * occurs when the link is set to up state. * Only the VRRP process is interested in link messages. */ #ifdef _WITH_VRRP_ #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_VRRP) #endif return netlink_link_filter(snl, h); #endif break; case RTM_NEWADDR: case RTM_DELADDR: return netlink_if_address_filter(snl, h); break; #ifdef _WITH_VRRP_ case RTM_NEWROUTE: case RTM_DELROUTE: return netlink_route_filter(snl, h); case RTM_NEWRULE: case RTM_DELRULE: return netlink_rule_filter(snl, h); #endif default: log_message(LOG_INFO, "Kernel is reflecting an unknown netlink nlmsg_type: %d", h->nlmsg_type); break; } return 0; } static void kernel_netlink(thread_ref_t thread) { nl_handle_t *nl = THREAD_ARG(thread); if (thread->type != THREAD_READ_TIMEOUT) netlink_parse_info(netlink_broadcast_filter, nl, NULL, true); nl->thread = thread_add_read(master, kernel_netlink, nl, nl->fd, TIMER_NEVER, 0); } #ifdef _WITH_VRRP_ void kernel_netlink_poll(void) { if (nl_kernel.fd < 0) return; netlink_parse_info(netlink_broadcast_filter, &nl_kernel, NULL, true); } #endif void kernel_netlink_set_recv_bufs(void) { #ifdef _ONE_PROCESS_DEBUG_ #ifdef _WITH_VRRP_ netlink_set_rx_buf_size(&nl_kernel, global_data->vrrp_netlink_monitor_rcv_bufs, global_data->vrrp_netlink_monitor_rcv_bufs_force); netlink_set_rx_buf_size(&nl_cmd, global_data->vrrp_netlink_cmd_rcv_bufs, global_data->vrrp_netlink_cmd_rcv_bufs_force); #else netlink_set_rx_buf_size(&nl_kernel, global_data->lvs_netlink_monitor_rcv_bufs, global_data->lvs_netlink_monitor_rcv_bufs_force); netlink_set_rx_buf_size(&nl_cmd, global_data->lvs_netlink_cmd_rcv_bufs, global_data->lvs_netlink_cmd_rcv_bufs_force); #endif #else #ifdef _WITH_VRRP_ if (prog_type == PROG_TYPE_VRRP) { netlink_set_rx_buf_size(&nl_kernel, global_data->vrrp_netlink_monitor_rcv_bufs, global_data->vrrp_netlink_monitor_rcv_bufs_force); netlink_set_rx_buf_size(&nl_cmd, global_data->vrrp_netlink_cmd_rcv_bufs, global_data->vrrp_netlink_cmd_rcv_bufs_force); } #endif #ifdef _WITH_LVS_ if (prog_type == PROG_TYPE_CHECKER) netlink_set_rx_buf_size(&nl_kernel, global_data->lvs_netlink_monitor_rcv_bufs, global_data->lvs_netlink_monitor_rcv_bufs_force); #endif #endif } void kernel_netlink_close_monitor(void) { netlink_close(&nl_kernel); } void kernel_netlink_close_cmd(void) { netlink_close(&nl_cmd); } void kernel_netlink_close(void) { kernel_netlink_close_monitor(); kernel_netlink_close_cmd(); } void kernel_netlink_init(void) { /* * Prepare netlink kernel broadcast channel * subscription. We subscribe to LINK, ADDR, * and ROUTE netlink broadcast messages, but * the checker process does not need the * route or link messages. */ /* If the netlink kernel fd is already open, just register a read thread. * This will happen at reload. */ if (nl_kernel.fd >= 0) { nl_kernel.thread = thread_add_read(master, kernel_netlink, &nl_kernel, nl_kernel.fd, TIMER_NEVER, 0); return; } #ifdef _ONE_PROCESS_DEBUG_ #ifdef _WITH_VRRP_ netlink_socket(&nl_kernel, global_data->vrrp_netlink_monitor_rcv_bufs, global_data->vrrp_netlink_monitor_rcv_bufs_force, SOCK_NONBLOCK, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0); #else netlink_socket(&nl_kernel, global_data->lvs_netlink_monitor_rcv_bufs, global_data->lvs_netlink_monitor_rcv_bufs_force, SOCK_NONBLOCK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0); #endif #else #ifdef _WITH_VRRP_ if (prog_type == PROG_TYPE_VRRP) netlink_socket(&nl_kernel, global_data->vrrp_netlink_monitor_rcv_bufs, global_data->vrrp_netlink_monitor_rcv_bufs_force, SOCK_NONBLOCK, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0); #endif #ifdef _WITH_LVS_ if (prog_type == PROG_TYPE_CHECKER) netlink_socket(&nl_kernel, global_data->lvs_netlink_monitor_rcv_bufs, global_data->lvs_netlink_monitor_rcv_bufs_force, SOCK_NONBLOCK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0); #endif #endif if (nl_kernel.fd >= 0) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Registering Kernel netlink reflector"); nl_kernel.thread = thread_add_read(master, kernel_netlink, &nl_kernel, nl_kernel.fd, TIMER_NEVER, 0); } else log_message(LOG_INFO, "Error while registering Kernel netlink reflector channel"); /* Prepare netlink command channel. The cmd socket is used synchronously.*/ #ifdef _ONE_PROCESS_DEBUG_ #ifdef _WITH_VRRP_ netlink_socket(&nl_cmd, global_data->vrrp_netlink_cmd_rcv_bufs, global_data->vrrp_netlink_cmd_rcv_bufs_force, 0, 0); #else netlink_socket(&nl_cmd, global_data->lvs_netlink_cmd_rcv_bufs, global_data->lvs_netlink_cmd_rcv_bufs_force, 0, 0); #endif #else #ifdef _WITH_VRRP_ if (prog_type == PROG_TYPE_VRRP) netlink_socket(&nl_cmd, global_data->vrrp_netlink_cmd_rcv_bufs, global_data->vrrp_netlink_cmd_rcv_bufs_force, 0, 0); #endif #ifdef _WITH_LVS_ if (prog_type == PROG_TYPE_CHECKER) netlink_socket(&nl_cmd, global_data->lvs_netlink_cmd_rcv_bufs, global_data->lvs_netlink_cmd_rcv_bufs_force, 0, 0); #endif #endif if (nl_cmd.fd >= 0) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Registering Kernel netlink command channel"); } else log_message(LOG_INFO, "Error while registering Kernel netlink cmd channel"); /* Start with netlink interface and address lookup */ #ifdef _WITH_VRRP_ #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_VRRP) #endif init_interface_queue(); #endif netlink_address_lookup(); #if !defined _ONE_PROCESS_DEBUG_ && defined _WITH_LVS_ if (prog_type == PROG_TYPE_CHECKER) kernel_netlink_close_cmd(); #endif } void cancel_kernel_netlink_threads(void) { if (nl_kernel.fd != -1 && nl_kernel.thread) { thread_cancel(nl_kernel.thread); nl_kernel.thread = NULL; } } #ifdef _WITH_VRRP_ void kernel_netlink_read_interfaces(void) { int ret; #ifdef _WITH_VRRP_ netlink_socket(&nl_cmd, global_data->vrrp_netlink_cmd_rcv_bufs, global_data->vrrp_netlink_cmd_rcv_bufs_force, 0, 0); #else netlink_socket(&nl_cmd, global_data->lvs_netlink_cmd_rcv_bufs, global_data->lvs_netlink_cmd_rcv_bufs_force, 0, 0); #endif if (nl_cmd.fd < 0) fprintf(stderr, "Error while registering Kernel netlink cmd channel\n"); init_interface_queue(); if ((ret = netlink_address_lookup())) fprintf(stderr, "netlink_address_lookup() returned %d\n", ret); kernel_netlink_close_cmd(); } #endif #ifdef THREAD_DUMP void register_keepalived_netlink_addresses(void) { register_thread_address("kernel_netlink", kernel_netlink); #ifdef _WITH_VRRP_ register_thread_address("delayed_if_flags_change_thread", delayed_if_flags_change_thread); #endif } #endif keepalived-2.3.3/keepalived/core/global_parser.c0000664000175000017500000023777314756615757015406 /* * Soft: Keepalived is a failover program for the LVS project * . 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, * * 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-2023 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WITH_SNMP_ #include "snmp.h" #endif #include "global_parser.h" #include "global_data.h" #include "main.h" #include "parser.h" #include "smtp.h" #include "utils.h" #include "logger.h" #include "bitops.h" #ifdef _WITH_FIREWALL_ #include "vrrp_firewall.h" #endif #include "memory.h" #ifdef _WITH_VRRP_ #include "vrrp_daemon.h" #ifdef _WITH_NFTABLES_ #include "vrrp_nftables.h" #endif #endif #ifdef _WITH_LVS_ #ifdef _WITH_NFTABLES_ #include "check_nftables.h" #endif #endif #include "namespaces.h" #ifdef _WITH_JSON_ #include "global_json.h" #endif /* Defined in kernel source file include/linux/sched.h but * not currently (Linux v5.10.12) exposed to userspace. * Also not currently exposed by glibc (v2.32). */ #ifndef TASK_COMM_LEN #define TASK_COMM_LEN 16 #endif /* data handlers */ /* Global def handlers */ #ifdef _WITH_LINKBEAT_ static void use_polling_handler(const vector_t *strvec) { if (!strvec) return; global_data->linkbeat_use_polling = true; } #endif static void save_process_name(char const **dest, const char *src) { size_t len; if (!src) { report_config_error(CONFIG_GENERAL_ERROR, "Process name missing"); return; } if (*dest) FREE_CONST_PTR(*dest); if ((len = strlen(src)) >= TASK_COMM_LEN) { report_config_error(CONFIG_GENERAL_ERROR, "Process name %s more than %d characters, truncating", src, TASK_COMM_LEN - 1); len = TASK_COMM_LEN - 1; } *dest = STRNDUP(src, len); } static void process_names_handler(__attribute__((unused)) const vector_t *strvec) { #ifdef _WITH_VRRP_ save_process_name(&global_data->vrrp_process_name, "keepalived_vrrp"); #endif #ifdef _WITH_LVS_ save_process_name(&global_data->lvs_process_name, "keepalived_lvs"); #endif #ifdef _WITH_BFD_ save_process_name(&global_data->bfd_process_name, "keepalived_bfd"); #endif } static void process_name_handler(const vector_t *strvec) { save_process_name(&global_data->process_name, strvec_slot(strvec, 1)); } #ifdef _WITH_VRRP_ static void vrrp_process_name_handler(const vector_t *strvec) { save_process_name(&global_data->vrrp_process_name, strvec_slot(strvec, 1)); } #endif #ifdef _WITH_LVS_ static void checker_process_name_handler(const vector_t *strvec) { save_process_name(&global_data->lvs_process_name, strvec_slot(strvec, 1)); } static void lvs_process_name_handler(const vector_t *strvec) { /* Deprecated since 12/07/20 */ log_message(LOG_INFO, "'lvs_process_name' is deprecated - please use 'checker_process_name'"); checker_process_name_handler(strvec); } #endif #ifdef _WITH_BFD_ static void bfd_process_name_handler(const vector_t *strvec) { save_process_name(&global_data->bfd_process_name, strvec_slot(strvec, 1)); } #endif static void use_symlink_path_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global use_symlink_path specified", strvec_slot(strvec, 1)); return; } } global_data->use_symlinks = res; } static void routerid_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "routerid name missing - ignoring"); return; } FREE_CONST_PTR(global_data->router_id); global_data->router_id = set_value(strvec); } static void emailfrom_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "emailfrom missing - ignoring"); return; } else if (vector_size(strvec) > 2) report_config_error(CONFIG_GENERAL_ERROR, "emailfrom - ignoring extra entries '%s' ...", strvec_slot(strvec, 2)); FREE_CONST_PTR(global_data->email_from); global_data->email_from = format_email_addr(strvec_slot(strvec, 1)); } static void smtpto_handler(const vector_t *strvec) { unsigned timeout; /* The min value should be 1, but allow 0 to maintain backward compatibility * with pre v2.0.7 */ if (!read_unsigned_strvec(strvec, 1, &timeout, 0, UINT_MAX / TIMER_HZ, true)) { report_config_error(CONFIG_GENERAL_ERROR, "smtp_connect_timeout '%s' must be in [0, %u] - ignoring", strvec_slot(strvec, 1), UINT_MAX / TIMER_HZ); return; } if (timeout == 0) { report_config_error(CONFIG_GENERAL_ERROR, "smtp_conect_timeout must be greater than 0, setting to 1"); timeout = 1; } global_data->smtp_connection_to = timeout * TIMER_HZ; } #ifdef _WITH_VRRP_ static void dynamic_interfaces_handler(const vector_t *strvec) { const char *str; global_data->dynamic_interfaces = true; if (vector_size(strvec) >= 2) { str = strvec_slot(strvec, 1); if (!strcmp(str, "allow_if_changes")) global_data->allow_if_changes = true; else report_config_error(CONFIG_GENERAL_ERROR, "Unknown dynamic_interfaces option '%s'",str); } } static void no_email_faults_handler(__attribute__((unused))const vector_t *strvec) { global_data->no_email_faults = true; } #endif static void smtpserver_handler(const vector_t *strvec) { bool ret = true; const char *port_str = SMTP_PORT_STR; /* Has a port number been specified? */ if (vector_size(strvec) >= 3) port_str = strvec_slot(strvec,2); /* It can't be an IP address if it contains '-' or '/' */ if (!strpbrk(strvec_slot(strvec, 1), "-/")) ret = inet_stosockaddr(strvec_slot(strvec, 1), port_str, &global_data->smtp_server); if (ret) domain_stosockaddr(strvec_slot(strvec, 1), port_str, &global_data->smtp_server); if (global_data->smtp_server.ss_family == AF_UNSPEC) report_config_error(CONFIG_GENERAL_ERROR, "Invalid smtp server %s %s", strvec_slot(strvec, 1), port_str); } static void smtphelo_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) return; global_data->smtp_helo_name = STRDUP(strvec_slot(strvec, 1)); } static void email_handler(const vector_t *strvec) { const vector_t *email_vec = read_value_block(strvec); unsigned int i; if (!email_vec) { report_config_error(CONFIG_GENERAL_ERROR, "Warning - empty notification_email block"); return; } for (i = 0; i < vector_size(email_vec); i++) alloc_email(vector_slot(email_vec, i)); free_strvec(email_vec); } static void smtp_alert_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global smtp_alert specified", strvec_slot(strvec, 1)); return; } } global_data->smtp_alert = res; } static void startup_shutdown_script(const vector_t *strvec, notify_script_t **script, bool startup) { const char *type = startup ? "startup" : "shutdown"; #ifndef _ONE_PROCESS_DEBUG_ if (prog_type != PROG_TYPE_PARENT) return; #endif if (*script) { report_config_error(CONFIG_GENERAL_ERROR, "%s script already specified", type); return; } if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "%s script missing", type); return; } if (!(*script = notify_script_init(0, type))) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid %s script", type); return; } if (startup) { if (!global_data->startup_script_timeout) global_data->startup_script_timeout = 10; } else { if (!global_data->shutdown_script_timeout) global_data->shutdown_script_timeout = 10; } } static void startup_shutdown_script_timeout_handler(const vector_t *strvec, bool startup) { const char *type = startup ? "startup" : "shutdown"; unsigned delay; #ifndef _ONE_PROCESS_DEBUG_ if (prog_type != PROG_TYPE_PARENT) return; #endif if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "%s_script_timeout requires value", type); return; } if (!read_unsigned_strvec(strvec, 1, &delay, 1, 1000, true)) { report_config_error(CONFIG_GENERAL_ERROR, "%s_script_timeout '%s' must be in [1, 1000] - ignoring", type, strvec_slot(strvec, 1)); return; } if (startup) global_data->startup_script_timeout = delay; else global_data->shutdown_script_timeout = delay; } static void startup_script_handler(const vector_t *strvec) { /* Only applicable for the parent process */ startup_shutdown_script(strvec, &global_data->startup_script, true); } static void startup_script_timeout_handler(const vector_t *strvec) { /* Only applicable for the parent process */ startup_shutdown_script_timeout_handler(strvec, true); } static void shutdown_script_handler(const vector_t *strvec) { startup_shutdown_script(strvec, &global_data->shutdown_script, false); } static void shutdown_script_timeout_handler(const vector_t *strvec) { startup_shutdown_script_timeout_handler(strvec, false); } static void max_auto_priority_handler(const vector_t *strvec) { int priority; int max_priority = sched_get_priority_max(SCHED_RR); if (vector_size(strvec) < 2) { global_data->max_auto_priority = max_priority; return; } if (!read_int_strvec(strvec, 1, &priority, -1, max_priority, true)) { report_config_error(CONFIG_GENERAL_ERROR, "max_auto_priority '%s' must be in [0, %d] (or -1 to disable) - ignoring", strvec_slot(strvec, 1), max_priority); return; } global_data->max_auto_priority = priority; } static void min_auto_priority_delay_handler(const vector_t *strvec) { unsigned delay; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "min_auto_priority_delay requires delay time"); return; } if (!read_unsigned_strvec(strvec, 1, &delay, 1U, 10000000U, true)) { report_config_error(CONFIG_GENERAL_ERROR, "min_auto_priority_delay '%s' must be in [1, 10000000] - ignoring", strvec_slot(strvec, 1)); return; } global_data->min_auto_priority_delay = delay; } #ifdef _WITH_VRRP_ static void smtp_alert_vrrp_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global smtp_alert_vrrp specified", strvec_slot(strvec, 1)); return; } } global_data->smtp_alert_vrrp = res; } #endif #ifdef _WITH_LVS_ static void smtp_alert_checker_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global smtp_alert_checker specified", strvec_slot(strvec, 1)); return; } } global_data->smtp_alert_checker = res; } static void checker_log_all_failures_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value for checker_log_all_failures specified"); return; } } global_data->checker_log_all_failures = res; } #endif #ifdef _WITH_VRRP_ static void default_interface_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "default_interface requires interface name"); return; } FREE_CONST_PTR(global_data->default_ifname); global_data->default_ifname = set_value(strvec); } static void disable_local_igmp_handler(__attribute__((unused)) const vector_t *strvec) { if (access(igmp_link_local_mcast_reports, W_OK)) { report_config_error(CONFIG_GENERAL_ERROR, "kernel does not support %s", igmp_link_local_mcast_reports); return; } global_data->disable_local_igmp = true; } static void v3_checksum_as_v2(__attribute__((unused)) const vector_t *strvec) { global_data->v3_checksum_as_v2 = true; } #endif #ifdef _WITH_LVS_ static void lvs_timeouts(const vector_t *strvec) { unsigned val; size_t i; if (vector_size(strvec) < 3) { report_config_error(CONFIG_GENERAL_ERROR, "lvs_timeouts requires at least one option"); return; } for (i = 1; i < vector_size(strvec); i++) { if (!strcmp(strvec_slot(strvec, i), "tcp")) { if (i == vector_size(strvec) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "No value specified for lvs_timeout tcp - ignoring"); continue; } if (!read_unsigned_strvec(strvec, i + 1, &val, 0, LVS_MAX_TIMEOUT, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_timeout tcp (%s) - ignoring", strvec_slot(strvec, i+1)); else global_data->lvs_timeouts.tcp_timeout = val; i++; /* skip over value */ continue; } if (!strcmp(strvec_slot(strvec, i), "tcpfin")) { if (i == vector_size(strvec) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "No value specified for lvs_timeout tcpfin - ignoring"); continue; } if (!read_unsigned_strvec(strvec, i + 1, &val, 0, LVS_MAX_TIMEOUT, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_timeout tcpfin (%s) - ignoring", strvec_slot(strvec, i+1)); else global_data->lvs_timeouts.tcp_fin_timeout = val; i++; /* skip over value */ continue; } if (!strcmp(strvec_slot(strvec, i), "udp")) { if (i == vector_size(strvec) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "No value specified for lvs_timeout udp - ignoring"); continue; } if (!read_unsigned_strvec(strvec, i + 1, &val, 0, LVS_MAX_TIMEOUT, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_timeout udp (%s) - ignoring", strvec_slot(strvec, i+1)); else global_data->lvs_timeouts.udp_timeout = val; i++; /* skip over value */ continue; } report_config_error(CONFIG_GENERAL_ERROR, "Unknown option %s specified for lvs_timeouts", strvec_slot(strvec, i)); } } #if defined _WITH_LVS_ && defined _WITH_VRRP_ static void lvs_syncd_handler(const vector_t *strvec) { unsigned val; size_t i; if (global_data->lvs_syncd.ifname) { report_config_error(CONFIG_GENERAL_ERROR, "lvs_sync_daemon has already been specified as %s %s - ignoring", global_data->lvs_syncd.ifname, global_data->lvs_syncd.vrrp_name ? global_data->lvs_syncd.vrrp_name : ""); return; } if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "lvs_sync_daemon requires interface"); return; } if (strlen(strvec_slot(strvec, 1)) >= IP_VS_IFNAME_MAXLEN) { report_config_error(CONFIG_GENERAL_ERROR, "lvs_sync_daemon interface name '%s' too long - ignoring", strvec_slot(strvec, 1)); return; } global_data->lvs_syncd.ifname = set_value(strvec); for (i = 2; i < vector_size(strvec); i++) { #ifdef _WITH_VRRP_ if (!strcmp(strvec_slot(strvec, i), "inst")) { if (global_data->lvs_syncd.vrrp_name) report_config_error(CONFIG_GENERAL_ERROR, "lvs_sync_daemon vrrp instance has already been specified as %s - ignoring", global_data->lvs_syncd.vrrp_name); else global_data->lvs_syncd.vrrp_name = STRDUP(strvec_slot(strvec, i + 1)); i++; /* skip over value */ continue; } #endif if (!strcmp(strvec_slot(strvec, i), "id")) { if (i == vector_size(strvec) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "No value specified for lvs_sync_daemon id, defaulting to vrid"); continue; } if (!read_unsigned_strvec(strvec, i + 1, &val, 0, 255, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid syncid (%s) - defaulting to vrid", strvec_slot(strvec, i+1)); else global_data->lvs_syncd.syncid = val; i++; /* skip over value */ continue; } #ifdef _HAVE_IPVS_SYNCD_ATTRIBUTES_ if (!strcmp(strvec_slot(strvec, i), "maxlen")) { if (i == vector_size(strvec) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "No value specified for lvs_sync_daemon maxlen - ignoring"); continue; } if (!read_unsigned_strvec(strvec, i + 1, &val, 1, 65535 - 20 - 8, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_sync_daemon maxlen (%s) - ignoring", strvec_slot(strvec, i+1)); else global_data->lvs_syncd.sync_maxlen = (uint16_t)val; i++; /* skip over value */ continue; } if (!strcmp(strvec_slot(strvec, i), "port")) { if (i == vector_size(strvec) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "No value specified for lvs_sync_daemon port - ignoring"); continue; } if (!read_unsigned_strvec(strvec, i + 1, &val, 1, 65535, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_sync_daemon port (%s) - ignoring", strvec_slot(strvec, i+1)); else global_data->lvs_syncd.mcast_port = (uint16_t)val; i++; /* skip over value */ continue; } if (!strcmp(strvec_slot(strvec, i), "ttl")) { if (i == vector_size(strvec) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "No value specified for lvs_sync_daemon ttl - ignoring"); continue; } if (!read_unsigned_strvec(strvec, i + 1, &val, 1, 255, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_sync_daemon ttl (%s) - ignoring", strvec_slot(strvec, i+1)); else global_data->lvs_syncd.mcast_ttl = (uint8_t)val; i++; /* skip over value */ continue; } if (!strcmp(strvec_slot(strvec, i), "group")) { if (i == vector_size(strvec) - 1) { report_config_error(CONFIG_GENERAL_ERROR, "No value specified for lvs_sync_daemon group - ignoring"); continue; } if (inet_stosockaddr(strvec_slot(strvec, i+1), NULL, &global_data->lvs_syncd.mcast_group)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_sync_daemon group (%s) - ignoring", strvec_slot(strvec, i+1)); if ((global_data->lvs_syncd.mcast_group.ss_family == AF_INET && !IN_MULTICAST(htonl(PTR_CAST(struct sockaddr_in, &global_data->lvs_syncd.mcast_group)->sin_addr.s_addr))) || (global_data->lvs_syncd.mcast_group.ss_family == AF_INET6 && !IN6_IS_ADDR_MULTICAST(&PTR_CAST(struct sockaddr_in6, &global_data->lvs_syncd.mcast_group)->sin6_addr))) { report_config_error(CONFIG_GENERAL_ERROR, "lvs_sync_daemon group address %s is not multicast - ignoring", strvec_slot(strvec, i+1)); global_data->lvs_syncd.mcast_group.ss_family = AF_UNSPEC; } i++; /* skip over value */ continue; } #endif /* The following are for backward compatibility when lvs_sync_daemon IF VRRP_INSTANCE [SYNC_ID] could be specified */ if (i == 2) { global_data->lvs_syncd.vrrp_name = STRDUP(strvec_slot(strvec, 2)); continue; } if (i == 3) { if (!read_unsigned_strvec(strvec, 3, &val, 0, 255, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid syncid (%s) - defaulting to vrid", strvec_slot(strvec, 3)); else { report_config_error(CONFIG_GENERAL_ERROR, "Please use keyword \"id\" before lvs_sync_daemon SYNCID"); global_data->lvs_syncd.syncid = val; } continue; } /* We haven't matched anything */ report_config_error(CONFIG_GENERAL_ERROR, "Unknown option %s specified for lvs_sync_daemon", strvec_slot(strvec, i)); } } #endif static void lvs_flush_handler(__attribute__((unused)) const vector_t *strvec) { global_data->lvs_flush = true; } static void lvs_flush_on_stop_handler(const vector_t *strvec) { if (vector_size(strvec) == 1) global_data->lvs_flush_on_stop = LVS_FLUSH_FULL; else if (!strcmp(strvec_slot(strvec, 1), "VS")) global_data->lvs_flush_on_stop = LVS_FLUSH_VS; else report_config_error(CONFIG_GENERAL_ERROR, "Unknown lvs_flush_on_stop type %s", strvec_slot(strvec, 1)); } #endif static int get_realtime_priority(const vector_t *strvec, const char *process) { int min_priority; int max_priority; int priority; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No %s process real-time priority specified", process); return -1; } min_priority = sched_get_priority_min(SCHED_RR); max_priority = sched_get_priority_max(SCHED_RR); if (!read_int_strvec(strvec, 1, &priority, INT_MIN, INT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "%s process real-time priority '%s' invalid", process, strvec_slot(strvec, 1)); return -1; } if (priority < min_priority) { report_config_error(CONFIG_GENERAL_ERROR, "%s process real-time priority %d less than minimum %d - setting to minimum", process, priority, min_priority); priority = min_priority; } else if (priority > max_priority) { report_config_error(CONFIG_GENERAL_ERROR, "%s process real-time priority %d greater than maximum %d - setting to maximum", process, priority, max_priority); priority = max_priority; } return priority; } static int get_cpu_affinity(const vector_t *strvec, cpu_set_t *set, const char *process) { int cpu_id, num_cpus; unsigned i; if (!strvec) return -1; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No %s cpu_id set specified", process); return -1; } CPU_ZERO(set); /* TODO: instead of sysconf, maybe we could fetch current cpu_set via * sched_getaffinity and use CPU_COUNT */ num_cpus = sysconf(_SC_NPROCESSORS_ONLN); for (i = 1; i < vector_size(strvec); i++) { if (!read_int_strvec(strvec, i, &cpu_id, 0, num_cpus-1, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid cpu_id:%d specified for %s process" , cpu_id, process); /* Reset cpu_set at first error */ CPU_ZERO(set); return -1; } CPU_SET(cpu_id, set); } return 0; } static rlim_t get_rt_rlimit(const vector_t *strvec, const char *process) { unsigned limit; rlim_t rlim; size_t keyword_len; /* *_rlimit_rtime is deprecated since 02/02/2020. Keyword should be *_rlimit_rttime */ keyword_len = strlen(strvec_slot(strvec, 0)); if (strvec_slot(strvec, 0)[keyword_len - 5] == 'r') log_message(LOG_INFO, "Keyword '%s' is deprecated - please use '%.*srttime'", strvec_slot(strvec, 0), (int)keyword_len - 5, strvec_slot(strvec, 0)); if (!read_unsigned_strvec(strvec, 1, &limit, 1, UINT32_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid %s real-time limit - %s", process, strvec_slot(strvec, 1)); return 0; } /* The rlim value is divided by 2 elsewhere, and the result must be * non-zero, therefore we need rlim to have a minimum value of 2. */ if (limit == 1) limit = 2; rlim = limit; return rlim; } static int8_t get_priority(const vector_t *strvec, const char *process) { int priority; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No %s process priority specified", process); return 0; } if (!read_int_strvec(strvec, 1, &priority, -20, 19, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid %s process priority specified", process); return 0; } return (int8_t)priority; } #ifdef _WITH_VRRP_ static void vrrp_mcast_group4_handler(const vector_t *strvec) { sockaddr_t mcast = { .ss_family = AF_UNSPEC }; if (inet_stosockaddr(strvec_slot(strvec, 1), NULL, &mcast)) { report_config_error(CONFIG_GENERAL_ERROR, "Can't parse vrrp_mcast_group4 [%s]. Skipping" , strvec_slot(strvec, 1)); return; } if (mcast.ss_family != AF_INET) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_mcast_group4 [%s] is not IPv4. Skipping" , strvec_slot(strvec, 1)); return; } /* Check the address is multicast */ if (!IN_MULTICAST(htonl(PTR_CAST(struct sockaddr_in, &mcast)->sin_addr.s_addr))) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_mcast_group4 [%s] is not multicast. Skipping" , strvec_slot(strvec, 1)); return; } global_data->vrrp_mcast_group4 = *PTR_CAST(struct sockaddr_in, &mcast); } static void vrrp_mcast_group6_handler(const vector_t *strvec) { sockaddr_t mcast = { .ss_family = AF_UNSPEC }; if (inet_stosockaddr(strvec_slot(strvec, 1), NULL, &mcast)) { report_config_error(CONFIG_GENERAL_ERROR, "Can't parse vrrp_mcast_group6 [%s]. Skipping" , strvec_slot(strvec, 1)); return; } if (mcast.ss_family != AF_INET6) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_mcast_group6 [%s] is not IPv6. Skipping" , strvec_slot(strvec, 1)); return; } /* Check the address is multicast */ if (!IN6_IS_ADDR_MULTICAST(&PTR_CAST(struct sockaddr_in6, &mcast)->sin6_addr)) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_mcast_group6 [%s] is not multicast. Skipping" , strvec_slot(strvec, 1)); return; } /* An IPv6 multicast address should be link local */ if (!IN6_IS_ADDR_MC_LINKLOCAL(&PTR_CAST(struct sockaddr_in6, &mcast)->sin6_addr)) report_config_error(CONFIG_WARNING, "vrrp_mcast_group6 [%s] should be link-local multicast." , strvec_slot(strvec, 1)); global_data->vrrp_mcast_group6 = *PTR_CAST(struct sockaddr_in6, &mcast); } static void vrrp_garp_delay_handler(const vector_t *strvec) { unsigned timeout; if (!read_unsigned_strvec(strvec, 1, &timeout, 0, UINT_MAX / TIMER_HZ, true)) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_garp_master_delay '%s' invalid - ignoring", strvec_slot(strvec, 1)); return; } global_data->vrrp_garp_delay = timeout * TIMER_HZ; } static void vrrp_garp_rep_handler(const vector_t *strvec) { unsigned repeats; /* The min value should be 1, but allow 0 to maintain backward compatibility * with pre v2.0.7 */ if (!read_unsigned_strvec(strvec, 1, &repeats, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_garp_master_repeat '%s' invalid - ignoring", strvec_slot(strvec, 1)); return; } if (repeats == 0) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_garp_master_repeat must be greater than 0, setting to 1"); repeats = 1; } global_data->vrrp_garp_rep = repeats; } static void vrrp_garp_refresh_handler(const vector_t *strvec) { unsigned refresh; if (!read_unsigned_strvec(strvec, 1, &refresh, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid vrrp_garp_master_refresh '%s' - ignoring", strvec_slot(strvec, 1)); global_data->vrrp_garp_refresh.tv_sec = 0; } else global_data->vrrp_garp_refresh.tv_sec = refresh; global_data->vrrp_garp_refresh.tv_usec = 0; } static void vrrp_garp_refresh_rep_handler(const vector_t *strvec) { unsigned repeats; /* The min value should be 1, but allow 0 to maintain backward compatibility * with pre v2.0.7 */ if (!read_unsigned_strvec(strvec, 1, &repeats, 0, UINT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_garp_master_refresh_repeat '%s' invalid - ignoring", strvec_slot(strvec, 1)); return; } if (repeats == 0) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_garp_master_refresh_repeat must be greater than 0, setting to 1"); repeats = 1; } global_data->vrrp_garp_refresh_rep = repeats; } static void vrrp_garp_lower_prio_delay_handler(const vector_t *strvec) { unsigned delay; if (!read_unsigned_strvec(strvec, 1, &delay, 0, UINT_MAX / TIMER_HZ, true)) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_garp_lower_prio_delay '%s' invalid - ignoring", strvec_slot(strvec, 1)); return; } global_data->vrrp_garp_lower_prio_delay = delay * TIMER_HZ; } static void vrrp_garp_lower_prio_rep_handler(const vector_t *strvec) { unsigned garp_lower_prio_rep; if (!read_unsigned_strvec(strvec, 1, &garp_lower_prio_rep, 0, INT_MAX, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid vrrp_garp_lower_prio_repeat '%s'", strvec_slot(strvec, 1)); return; } global_data->vrrp_garp_lower_prio_rep = garp_lower_prio_rep; } static void vrrp_down_timer_adverts_handler(const vector_t *strvec) { unsigned down_timer_adverts; if (!read_unsigned_strvec(strvec, 1, &down_timer_adverts, 1, 100, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid vrrp_down_timer_adverts [1:100] '%s'", strvec_slot(strvec, 1)); return; } global_data->vrrp_down_timer_adverts = down_timer_adverts; } static void vrrp_garp_interval_handler(const vector_t *strvec) { unsigned interval; if (!read_decimal_unsigned_strvec(strvec, 1, &interval, 1, UINT_MAX, TIMER_HZ_DIGITS, true)) report_config_error(CONFIG_GENERAL_ERROR, "vrrp_garp_interval '%s' is invalid", strvec_slot(strvec, 1)); else global_data->vrrp_garp_interval = interval; if (global_data->vrrp_garp_interval >= 1 * TIMER_HZ) log_message(LOG_INFO, "The vrrp_garp_interval is very large - %s seconds", strvec_slot(strvec, 1)); } static void vrrp_gna_interval_handler(const vector_t *strvec) { unsigned interval; if (!read_decimal_unsigned_strvec(strvec, 1, &interval, 1, UINT_MAX, TIMER_HZ_DIGITS, true)) report_config_error(CONFIG_GENERAL_ERROR, "vrrp_gna_interval '%s' is invalid", strvec_slot(strvec, 1)); else global_data->vrrp_gna_interval = interval; if (global_data->vrrp_gna_interval >= 1 * TIMER_HZ) log_message(LOG_INFO, "The vrrp_gna_interval is very large - %s seconds", strvec_slot(strvec, 1)); } static void vrrp_min_garp_handler(const vector_t *strvec) { int res = false; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value for vrrp_min_garp specified"); return; } if (!res) return; } /* Set to only send 1 gratuitous ARP/NA message with no repeat, but don't * overwrite any parameters already set. */ if (global_data->vrrp_garp_rep == VRRP_GARP_REP) global_data->vrrp_garp_rep = 1; if (global_data->vrrp_garp_refresh.tv_sec == VRRP_GARP_REFRESH) global_data->vrrp_garp_refresh.tv_sec = 0; if (global_data->vrrp_garp_refresh_rep == VRRP_GARP_REFRESH_REP) global_data->vrrp_garp_refresh_rep = 0; if (global_data->vrrp_garp_delay == VRRP_GARP_DELAY) global_data->vrrp_garp_delay = 0; } #ifdef _HAVE_VRRP_VMAC_ static void vrrp_vmac_garp_extra_if_handler(const vector_t *strvec) { unsigned delay = 0; unsigned index; const char *cmd_name = strvec_slot(strvec, 0); if (!strcmp(cmd_name, "vrrp_vmac_garp_intvl")) { /* Deprecated after v2.2.2 */ report_config_error(CONFIG_DEPRECATED, "Keyword \"vrrp_vmac_garp_intvl\" is deprecated - please use \"vrrp_garp_extra_if\""); } for (index = 1; index < vector_size(strvec); index++) { if (!strcmp(strvec_slot(strvec, index), "all")) global_data->vrrp_vmac_garp_all_if = true; else if (!read_unsigned_strvec(strvec, index, &delay, 1, 86400, true)) { report_config_error(CONFIG_GENERAL_ERROR, "%s '%s' invalid - ignoring", cmd_name, strvec_slot(strvec, index)); return; } } if (!delay) { report_config_error(CONFIG_GENERAL_ERROR, "%s specified without time - ignoring", cmd_name); return; } global_data->vrrp_vmac_garp_intvl = delay; } #endif static void vrrp_lower_prio_no_advert_handler(const vector_t *strvec) { int res; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) report_config_error(CONFIG_GENERAL_ERROR, "Invalid value for vrrp_lower_prio_no_advert specified"); else global_data->vrrp_lower_prio_no_advert = res; } else global_data->vrrp_lower_prio_no_advert = true; } static void vrrp_higher_prio_send_advert_handler(const vector_t *strvec) { int res; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) report_config_error(CONFIG_GENERAL_ERROR, "Invalid value for vrrp_higher_prio_send_advert specified"); else global_data->vrrp_higher_prio_send_advert = res; } else global_data->vrrp_higher_prio_send_advert = true; } #endif #if defined _WITH_IPTABLES_ || defined _WITH_NFTABLES_ static bool check_valid_iptables_ipset_nftables_name(const vector_t *strvec, unsigned entry, unsigned max_len, const char *type_name, const char *log_name) { if (strlen(strvec_slot(strvec, entry)) >= max_len - 1) { report_config_error(CONFIG_GENERAL_ERROR, "VRRP Error : %s %s name too long - ignored", type_name, log_name); return false; } if (strlen(strvec_slot(strvec, entry)) == 0) { report_config_error(CONFIG_GENERAL_ERROR, "VRRP Error : %s %s name empty - ignored", type_name, log_name); return false; } return true; } #endif #ifdef _WITH_VRRP_ #ifdef _WITH_IPTABLES_ static bool check_valid_iptables_chain_name(const vector_t *strvec, unsigned entry, const char *log_name) { return check_valid_iptables_ipset_nftables_name(strvec, entry, XT_EXTENSION_MAXNAMELEN, "iptables", log_name); } static void vrrp_iptables_handler(const vector_t *strvec) { if (global_data->vrrp_iptables_inchain) { report_config_error(CONFIG_GENERAL_ERROR, "iptables already specified - ignoring"); return; } if (vector_size(strvec) >= 2) { if (!check_valid_iptables_chain_name(strvec, 1, "in chain")) return; global_data->vrrp_iptables_inchain = STRDUP(strvec_slot(strvec,1)); if (vector_size(strvec) >= 3) { if (!check_valid_iptables_chain_name(strvec, 2, "out chain")) return; if (!strcmp(global_data->vrrp_iptables_inchain, strvec_slot(strvec, 2))) { log_message(LOG_INFO, "vrrp_iptables: chain names cannot be the same"); FREE_CONST_PTR(global_data->vrrp_iptables_inchain); return; } global_data->vrrp_iptables_outchain = STRDUP(strvec_slot(strvec,2)); } return; } global_data->vrrp_iptables_inchain = STRDUP(DEFAULT_IPTABLES_CHAIN_IN); global_data->vrrp_iptables_outchain = STRDUP(DEFAULT_IPTABLES_CHAIN_OUT); } #ifdef _HAVE_LIBIPSET_ static bool check_valid_ipset_name(const vector_t *strvec, unsigned entry, const char *log_name) { return check_valid_iptables_ipset_nftables_name(strvec, entry, IPSET_MAXNAMELEN, "ipset", log_name); } static void vrrp_ipsets_handler(const vector_t *strvec) { size_t len; char set_name[IPSET_MAXNAMELEN]; unsigned sn0, sn1; const char **set_names[] = { &global_data->vrrp_ipset_address, &global_data->vrrp_ipset_address6, &global_data->vrrp_ipset_address_iface6, &global_data->vrrp_ipset_igmp, &global_data->vrrp_ipset_mld, #ifdef _HAVE_VRRP_VMAC_ &global_data->vrrp_ipset_vmac_nd #endif }; FREE_CONST_PTR(global_data->vrrp_ipset_address); FREE_CONST_PTR(global_data->vrrp_ipset_address6); FREE_CONST_PTR(global_data->vrrp_ipset_address_iface6); FREE_CONST_PTR(global_data->vrrp_ipset_igmp); FREE_CONST_PTR(global_data->vrrp_ipset_mld); #ifdef _HAVE_VRRP_VMAC_ FREE_CONST_PTR(global_data->vrrp_ipset_vmac_nd); #endif global_data->using_ipsets = PARAMETER_UNSET; if (vector_size(strvec) < 2) { global_data->using_ipsets = false; return; } if (!check_valid_ipset_name(strvec, 1, "address")) return; global_data->vrrp_ipset_address = STRDUP(strvec_slot(strvec,1)); if (vector_size(strvec) >= 3) { if (!check_valid_ipset_name(strvec, 2, "IPv6 address")) goto ipset_error; global_data->vrrp_ipset_address6 = STRDUP(strvec_slot(strvec,2)); } else { /* No second set specified, copy first name and add "6" */ strcpy_safe(set_name, global_data->vrrp_ipset_address); set_name[IPSET_MAXNAMELEN - 2] = '\0'; strcat(set_name, "6"); global_data->vrrp_ipset_address6 = STRDUP(set_name); } if (vector_size(strvec) >= 4) { if (!check_valid_ipset_name(strvec, 3, "IPv6 address_iface")) goto ipset_error; global_data->vrrp_ipset_address_iface6 = STRDUP(strvec_slot(strvec,3)); } else { /* No third set specified, copy second name and add "_if6" */ strcpy_safe(set_name, global_data->vrrp_ipset_address6); len = strlen(set_name); if (set_name[len-1] == '6') set_name[--len] = '\0'; set_name[IPSET_MAXNAMELEN - 5] = '\0'; strcat(set_name, "_if6"); global_data->vrrp_ipset_address_iface6 = STRDUP(set_name); } if (vector_size(strvec) >= 5) { if (!check_valid_ipset_name(strvec, 4, "IGMP")) goto ipset_error; global_data->vrrp_ipset_igmp = STRDUP(strvec_slot(strvec,4)); } else { /* No second set specified, copy first name and add "_igmp" */ strcpy_safe(set_name, global_data->vrrp_ipset_address); set_name[sizeof(set_name) - 6] = '\0'; strcat(set_name, "_igmp"); global_data->vrrp_ipset_igmp = STRDUP(set_name); } if (vector_size(strvec) >= 6) { if (!check_valid_ipset_name(strvec, 5, "MLD")) goto ipset_error; global_data->vrrp_ipset_mld = STRDUP(strvec_slot(strvec,5)); } else { /* No second set specified, copy first name and add "_mld" */ strcpy_safe(set_name, global_data->vrrp_ipset_address); set_name[sizeof(set_name) - 5] = '\0'; strcat(set_name, "_mld"); global_data->vrrp_ipset_mld = STRDUP(set_name); } #ifdef _HAVE_VRRP_VMAC_ if (vector_size(strvec) >= 7) { if (!check_valid_ipset_name(strvec, 6, "ND")) goto ipset_error; global_data->vrrp_ipset_vmac_nd = STRDUP(strvec_slot(strvec,6)); } else { /* No second set specified, copy first name and add "_nd" */ strcpy_safe(set_name, global_data->vrrp_ipset_address); set_name[sizeof(set_name) - 5] = '\0'; strcat(set_name, "_nd"); global_data->vrrp_ipset_vmac_nd = STRDUP(set_name); } #endif /* Ensure all the set names are different */ for (sn0 = 0; sn0 < sizeof(set_names) / sizeof(set_names[0]) - 1; sn0++) { for (sn1 = sn0 + 1; sn1 < sizeof(set_names) / sizeof(set_names[0]); sn1++) { if (!strcmp(*set_names[sn0], *set_names[sn1])) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_ipsets: set name %s used more than once", *set_names[sn0]); goto ipset_error; } } } global_data->using_ipsets = true; return; ipset_error: FREE_CONST_PTR(global_data->vrrp_ipset_address); FREE_CONST_PTR(global_data->vrrp_ipset_address6); FREE_CONST_PTR(global_data->vrrp_ipset_address_iface6); FREE_CONST_PTR(global_data->vrrp_ipset_igmp); FREE_CONST_PTR(global_data->vrrp_ipset_mld); #ifdef _HAVE_VRRP_VMAC_ FREE_CONST_PTR(global_data->vrrp_ipset_vmac_nd); #endif } #endif #elif defined _WITH_NFTABLES_ /* Allow legacy vrrp_iptables/vrrp_ipsets global_defs config to use nftables */ static void vrrp_iptables_handler(__attribute__((unused)) const vector_t *strvec) { report_config_error(CONFIG_GENERAL_ERROR, "iptables not supported, using nftables instead. Please replace 'vrrp_iptables and 'vrrp_ipsets' with 'nftables' config option"); /* Table name defaults to "keepalived" */ global_data->vrrp_nf_table_name = STRDUP(DEFAULT_NFTABLES_TABLE); global_data->vrrp_nf_chain_priority = -1; } #endif #endif #ifdef _WITH_NFTABLES_ 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) { const char *name; if (global_data->vrrp_nf_table_name) { report_config_error(CONFIG_GENERAL_ERROR, "nftables already specified - ignoring"); return; } if (vector_size(strvec) >= 2) { if (!check_valid_nftables_chain_name(strvec, 1, "chain")) return; name = strvec_slot(strvec, 1); } else { /* Table name defaults to "keepalived" */ name = DEFAULT_NFTABLES_TABLE; } global_data->vrrp_nf_table_name = STRDUP(name); global_data->vrrp_nf_chain_priority = -1; } static void vrrp_nftables_priority_handler(const vector_t *strvec) { int priority; if (read_int_strvec(strvec, 1, &priority, INT32_MIN, INT32_MAX, false)) global_data->vrrp_nf_chain_priority = priority; else report_config_error(CONFIG_INVALID_NUMBER, "invalid nftables chain priority '%s'", strvec_slot(strvec, 1)); } static void vrrp_nftables_ifindex_handler(__attribute__((unused)) const vector_t *strvec) { global_data->vrrp_nf_ifindex = true; } #endif #ifdef _WITH_LVS_ static void ipvs_nftables_handler(__attribute__((unused)) const vector_t *strvec) { const char *name; if (global_data->ipvs_nf_table_name) { report_config_error(CONFIG_GENERAL_ERROR, "ipvs nftables already specified - ignoring"); return; } if (vector_size(strvec) >= 2) { if (!check_valid_nftables_chain_name(strvec, 1, "ipvs chain")) return; name = strvec_slot(strvec, 1); } else { /* Table named defaults to "keepalived_ipvs" */ name = DEFAULT_NFTABLES_IPVS_TABLE; } global_data->ipvs_nf_table_name = STRDUP(name); global_data->ipvs_nf_chain_priority = -1; global_data->ipvs_nftables_start_fwmark = DEFAULT_IPVS_NF_START_FWMARK; } static void ipvs_nftables_priority_handler(const vector_t *strvec) { int priority; if (read_int_strvec(strvec, 1, &priority, INT32_MIN, INT32_MAX, false)) global_data->ipvs_nf_chain_priority = priority; else report_config_error(CONFIG_INVALID_NUMBER, "invalid ipvs nftables chain priority '%s'", strvec_slot(strvec, 1)); } static void ipvs_nftables_start_fwmark_handler(const vector_t *strvec) { unsigned fwmark; if (read_unsigned_strvec(strvec, 1, &fwmark, 1, UINT32_MAX, false)) global_data->ipvs_nftables_start_fwmark = fwmark; else report_config_error(CONFIG_INVALID_NUMBER, "invalid ipvs nftables start_fwmark priority '%s'", strvec_slot(strvec, 1)); } #endif static void 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) { int version; if (!read_int_strvec(strvec, 1, &version, 2, 3, true)) { report_config_error(CONFIG_GENERAL_ERROR, "VRRP Error: Version must be either 2 or 3"); return; } global_data->vrrp_version = version; } static void vrrp_check_unicast_src_handler(__attribute__((unused)) const vector_t *strvec) { global_data->vrrp_check_unicast_src = 1; } static void vrrp_check_adv_addr_handler(__attribute__((unused)) const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global vrrp_check_adv_addr specified", strvec_slot(strvec, 1)); return; } } global_data->vrrp_skip_check_adv_addr = res; } static void vrrp_strict_handler(__attribute__((unused)) const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global vrrp_strict specified", strvec_slot(strvec, 1)); return; } } global_data->vrrp_strict = res; } static void vrrp_prio_handler(const vector_t *strvec) { global_data->vrrp_process_priority = get_priority(strvec, "vrrp"); } static void vrrp_no_swap_handler(__attribute__((unused)) const vector_t *strvec) { global_data->vrrp_no_swap = true; } static void vrrp_rt_priority_handler(const vector_t *strvec) { int priority = get_realtime_priority(strvec, "vrrp"); if (priority >= 0) global_data->vrrp_realtime_priority = priority; } static void vrrp_cpu_affinity_handler(const vector_t *strvec) { get_cpu_affinity(strvec, &global_data->vrrp_cpu_mask, "vrrp"); } static void vrrp_rt_rlimit_handler(const vector_t *strvec) { global_data->vrrp_rlimit_rt = get_rt_rlimit(strvec, "vrrp"); } #endif static void notify_fifo(const vector_t *strvec, const char *type, notify_fifo_t *fifo) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No %snotify_fifo name specified", type); return; } if (fifo->name) { report_config_error(CONFIG_GENERAL_ERROR, "%snotify_fifo already specified - ignoring %s", type, strvec_slot(strvec,1)); return; } if (vector_size(strvec) > 2) { if (set_script_uid_gid(strvec, 2, &fifo->uid, &fifo->gid)) { log_message(LOG_INFO, "Invalid user/group for %s fifo %s - ignoring", type, fifo->name); return; } } else if (get_default_script_user(&fifo->uid, &fifo->gid)) { log_message(LOG_INFO, "Failed to set default user for %s fifo %s - ignoring", type, fifo->name); return; } fifo->name = STRDUP(strvec_slot(strvec, 1)); } static void notify_fifo_script(const vector_t *strvec, const char *type, notify_fifo_t *fifo) { char *id_str; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No %snotify_fifo_script specified", type); return; } if (fifo->script) { report_config_error(CONFIG_GENERAL_ERROR, "%snotify_fifo_script already specified - ignoring %s", type, strvec_slot(strvec,1)); return; } id_str = MALLOC(strlen(type) + strlen("notify_fifo") + 1); strcpy(id_str, type); strcat(id_str, "notify_fifo"); fifo->script = notify_script_init(1, id_str); FREE(id_str); } static void global_notify_fifo(const vector_t *strvec) { notify_fifo(strvec, "", &global_data->notify_fifo); } static void global_notify_fifo_script(const vector_t *strvec) { notify_fifo_script(strvec, "", &global_data->notify_fifo); } #ifdef _WITH_VRRP_ static void vrrp_notify_fifo(const vector_t *strvec) { notify_fifo(strvec, "vrrp_", &global_data->vrrp_notify_fifo); } static void vrrp_notify_fifo_script(const vector_t *strvec) { notify_fifo_script(strvec, "vrrp_", &global_data->vrrp_notify_fifo); } static void vrrp_notify_priority_changes(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global vrrp_notify_priority_changes specified", strvec_slot(strvec, 1)); return; } } global_data->vrrp_notify_priority_changes = res; } static void fifo_write_vrrp_states_on_reload(__attribute__((unused))const vector_t *strvec) { global_data->fifo_write_vrrp_states_on_reload = true; } #endif #ifdef _WITH_LVS_ static void lvs_notify_fifo(const vector_t *strvec) { notify_fifo(strvec, "lvs_", &global_data->lvs_notify_fifo); } static void lvs_notify_fifo_script(const vector_t *strvec) { notify_fifo_script(strvec, "lvs_", &global_data->lvs_notify_fifo); } #endif #ifdef _WITH_LVS_ static void checker_prio_handler(const vector_t *strvec) { global_data->checker_process_priority = get_priority(strvec, "checker"); } static void checker_no_swap_handler(__attribute__((unused)) const vector_t *strvec) { global_data->checker_no_swap = true; } static void checker_rt_priority_handler(const vector_t *strvec) { int priority = get_realtime_priority(strvec, "checker"); if (priority >= 0) global_data->checker_realtime_priority = priority; } static void checker_cpu_affinity_handler(const vector_t *strvec) { get_cpu_affinity(strvec, &global_data->checker_cpu_mask, "checker"); } static void checker_rt_rlimit_handler(const vector_t *strvec) { global_data->checker_rlimit_rt = get_rt_rlimit(strvec, "checker"); } #endif #ifdef _WITH_BFD_ static void bfd_prio_handler(const vector_t *strvec) { global_data->bfd_process_priority = get_priority(strvec, "bfd"); } static void bfd_no_swap_handler(__attribute__((unused)) const vector_t *strvec) { global_data->bfd_no_swap = true; } static void bfd_rt_priority_handler(const vector_t *strvec) { int priority = get_realtime_priority(strvec, "bfd"); if (priority >= 0) global_data->bfd_realtime_priority = priority; } static void bfd_cpu_affinity_handler(const vector_t *strvec) { get_cpu_affinity(strvec, &global_data->bfd_cpu_mask, "bfd"); } static void bfd_rt_rlimit_handler(const vector_t *strvec) { global_data->bfd_rlimit_rt = get_rt_rlimit(strvec, "bfd"); } #endif #ifdef _WITH_SNMP_ static void snmp_socket_handler(const vector_t *strvec) { if (vector_size(strvec) > 2) { report_config_error(CONFIG_GENERAL_ERROR, "Too many parameters specified for snmp_socket - ignoring"); return; } if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "SNMP error : snmp socket name missing"); return; } if (strlen(strvec_slot(strvec,1)) > PATH_MAX - 1) { report_config_error(CONFIG_GENERAL_ERROR, "SNMP error : snmp socket name too long - ignored"); return; } if (global_data->snmp_socket) { report_config_error(CONFIG_GENERAL_ERROR, "SNMP socket already set to %s - ignoring", global_data->snmp_socket); return; } global_data->snmp_socket = STRDUP(strvec_slot(strvec, 1)); } static void trap_handler(__attribute__((unused)) const vector_t *strvec) { global_data->enable_traps = true; } #ifdef _WITH_SNMP_VRRP_ static void snmp_vrrp_handler(__attribute__((unused)) const vector_t *strvec) { global_data->enable_snmp_vrrp = true; } #endif #ifdef _WITH_SNMP_RFC_ static void snmp_rfc_handler(__attribute__((unused)) const vector_t *strvec) { #ifdef _WITH_SNMP_RFCV2_ global_data->enable_snmp_rfcv2 = true; #endif #ifdef _WITH_SNMP_RFCV3_ global_data->enable_snmp_rfcv3 = true; #endif } #endif #ifdef _WITH_SNMP_RFCV2_ static void snmp_rfcv2_handler(__attribute__((unused)) const vector_t *strvec) { global_data->enable_snmp_rfcv2 = true; } #endif #ifdef _WITH_SNMP_RFCV3_ static void snmp_rfcv3_handler(__attribute__((unused)) const vector_t *strvec) { global_data->enable_snmp_rfcv3 = true; } #endif #ifdef _WITH_SNMP_CHECKER_ static void snmp_checker_handler(__attribute__((unused)) const vector_t *strvec) { global_data->enable_snmp_checker = true; } static void snmp_vs_stats_update_interval_handler(const vector_t *strvec) { unsigned long interval; /* Valid range is 1 ms to 30s */ if (read_timer(strvec, 1, &interval, 1000, 30 * TIMER_HZ, true)) global_data->snmp_vs_stats_update_interval = interval; else report_config_error(CONFIG_GENERAL_ERROR, "snmp stats vs update interval '%s' invalid - ignoring", strvec_slot(strvec, 1)); } static void snmp_rs_stats_update_interval_handler(const vector_t *strvec) { unsigned long interval; /* Valid range is 1 ms to 30s */ if (read_timer(strvec, 1, &interval, 1000, 30 * TIMER_HZ, true)) global_data->snmp_rs_stats_update_interval = interval; else report_config_error(CONFIG_GENERAL_ERROR, "snmp stats vs update interval '%s' invalid - ignoring", strvec_slot(strvec, 1)); } #endif #endif static void net_namespace_handler(const vector_t *strvec) { if (!strvec) return; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "net_namespace name missing - ignoring"); return; } if (!global_data->network_namespace) { global_data->network_namespace = set_value(strvec); use_pid_dir = true; } else report_config_error(CONFIG_GENERAL_ERROR, "Duplicate net_namespace definition %s - ignoring", strvec_slot(strvec, 1)); } static void net_namespace_ipvs_handler(const vector_t *strvec) { if (!strvec) return; if (global_data->network_namespace_ipvs) { report_config_error(CONFIG_GENERAL_ERROR, "Duplicate net_namespace_ipvs definition %s - ignoring", strvec_slot(strvec, 1)); return; } /* No namespace name means default namespace */ if (vector_size(strvec) < 2) global_data->network_namespace_ipvs = STRDUP(""); else global_data->network_namespace_ipvs = set_value(strvec); } static void namespace_ipsets_handler(const vector_t *strvec) { if (!strvec) return; global_data->namespace_with_ipsets = true; } #ifdef _WITH_DBUS_ static void enable_dbus_handler(__attribute__((unused)) const vector_t *strvec) { global_data->enable_dbus = true; } static void dbus_service_name_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "dbus_service_name missing - ignoring"); return; } FREE_CONST_PTR(global_data->dbus_service_name); global_data->dbus_service_name = set_value(strvec); } static void dbus_no_interface_name_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "dbus_no_interface_name missing - ignoring"); return; } FREE_CONST_PTR(global_data->dbus_no_interface_name); global_data->dbus_no_interface_name = set_value(strvec); } #endif static void instance_handler(const vector_t *strvec) { if (!strvec) return; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "instance name missing - ignoring"); return; } if (!reload) { if (!global_data->instance_name) { global_data->instance_name = set_value(strvec); use_pid_dir = true; } else report_config_error(CONFIG_GENERAL_ERROR, "Duplicate instance definition %s - ignoring", strvec_slot(strvec, 1)); } } static void use_pid_dir_handler(const vector_t *strvec) { if (!strvec) return; use_pid_dir = true; } static void script_user_handler(const vector_t *strvec) { if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "No script username specified"); return; } if (set_default_script_user(strvec_slot(strvec, 1), vector_size(strvec) > 2 ? strvec_slot(strvec, 2) : NULL)) report_config_error(CONFIG_GENERAL_ERROR, "Error setting global script uid/gid"); } static void script_security_handler(__attribute__((unused)) const vector_t *strvec) { script_security = true; } static void child_wait_handler(const vector_t *strvec) { unsigned secs; if (!strvec) return; if (!read_unsigned_strvec(strvec, 1, &secs, 0, UINT_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid child_wait_time %s", strvec_slot(strvec, 1)); return; } child_wait_time = secs; } #ifdef _WITH_VRRP_ static void vrrp_rx_bufs_policy_handler(const vector_t *strvec) { unsigned rx_buf_size; unsigned i; if (!strvec) return; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "vrrp_rx_bufs_policy missing"); return; } for (i = 1; i < vector_size(strvec); i++) { if (!strcasecmp(strvec_slot(strvec, i), "MTU")) global_data->vrrp_rx_bufs_policy |= RX_BUFS_POLICY_MTU; else if (!strcasecmp(strvec_slot(strvec, i), "ADVERT")) global_data->vrrp_rx_bufs_policy |= RX_BUFS_POLICY_ADVERT; else { if (!read_unsigned_strvec(strvec, 1, &rx_buf_size, 0, UINT_MAX, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid vrrp_rx_bufs_policy %s", strvec_slot(strvec, i)); else { global_data->vrrp_rx_bufs_size = rx_buf_size; global_data->vrrp_rx_bufs_policy |= RX_BUFS_SIZE; } } } if ((global_data->vrrp_rx_bufs_policy & RX_BUFS_SIZE) && (global_data->vrrp_rx_bufs_policy & (RX_BUFS_POLICY_MTU | RX_BUFS_POLICY_ADVERT))) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot set vrrp_rx_bufs_policy size and policy, ignoring policy"); global_data->vrrp_rx_bufs_policy &= ~(RX_BUFS_POLICY_MTU | RX_BUFS_POLICY_ADVERT); } else if ((global_data->vrrp_rx_bufs_policy & RX_BUFS_POLICY_MTU) && (global_data->vrrp_rx_bufs_policy & RX_BUFS_POLICY_ADVERT)) { report_config_error(CONFIG_GENERAL_ERROR, "Cannot set both vrrp_rx_bufs_policy MTU and ADVERT, ignoring ADVERT"); global_data->vrrp_rx_bufs_policy &= ~RX_BUFS_POLICY_ADVERT; } } static void vrrp_rx_bufs_multiplier_handler(const vector_t *strvec) { unsigned rx_buf_mult; if (!strvec) return; if (vector_size(strvec) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid vrrp_rx_bufs_multiplier"); return; } if (!read_unsigned_strvec(strvec, 1, &rx_buf_mult, 1, UINT_MAX, false)) report_config_error(CONFIG_GENERAL_ERROR, "Invalid vrrp_rx_bufs_multiplier %s", strvec_slot(strvec, 1)); else global_data->vrrp_rx_bufs_multiples = rx_buf_mult; } #endif #if defined _WITH_VRRP_ || defined _WITH_LVS_ static unsigned get_netlink_rcv_bufs_size(const vector_t *strvec, const char *type) { unsigned val; if (!strvec) return 0; if (vector_size(strvec) < 2) { report_config_error(CONFIG_GENERAL_ERROR, "%s_rcv_bufs size missing", type); return 0; } if (!read_unsigned_strvec(strvec, 1, &val, 0, UINT_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "%s_rcv_bufs size (%s) invalid", type, strvec_slot(strvec, 1)); return 0; } return val; } #endif #ifdef _WITH_VRRP_ static void vrrp_netlink_monitor_rcv_bufs_handler(const vector_t *strvec) { unsigned val; if (!strvec) return; val = get_netlink_rcv_bufs_size(strvec, "vrrp_netlink_monitor"); if (val) global_data->vrrp_netlink_monitor_rcv_bufs = val; } static void vrrp_netlink_monitor_rcv_bufs_force_handler(const vector_t *strvec) { int res = true; if (!strvec) return; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global vrrp_netlink_monitor_rcv_bufs_force specified", strvec_slot(strvec, 1)); return; } } global_data->vrrp_netlink_monitor_rcv_bufs_force = res; } static void vrrp_netlink_cmd_rcv_bufs_handler(const vector_t *strvec) { unsigned val; if (!strvec) return; val = get_netlink_rcv_bufs_size(strvec, "vrrp_netlink_cmd"); if (val) global_data->vrrp_netlink_cmd_rcv_bufs = val; } static void vrrp_netlink_cmd_rcv_bufs_force_handler(const vector_t *strvec) { int res = true; if (!strvec) return; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global vrrp_netlink_cmd_rcv_bufs_force specified", strvec_slot(strvec, 1)); return; } } global_data->vrrp_netlink_cmd_rcv_bufs_force = res; } #ifdef _WITH_TRACK_PROCESS_ static void process_monitor_rcv_bufs_handler(const vector_t *strvec) { unsigned val; if (!strvec) return; val = get_netlink_rcv_bufs_size(strvec, "process_monitor"); if (val) global_data->process_monitor_rcv_bufs = val; } static void process_monitor_rcv_bufs_force_handler(const vector_t *strvec) { int res = true; if (!strvec) return; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global process_monitor_rcv_bufs_force specified", strvec_slot(strvec, 1)); return; } } global_data->process_monitor_rcv_bufs_force = res; } #endif #endif #ifdef _WITH_LVS_ static void lvs_netlink_monitor_rcv_bufs_handler(const vector_t *strvec) { unsigned val; if (!strvec) return; val = get_netlink_rcv_bufs_size(strvec, "lvs_netlink_monitor"); if (val) global_data->lvs_netlink_monitor_rcv_bufs = val; } static void lvs_netlink_monitor_rcv_bufs_force_handler(const vector_t *strvec) { int res = true; if (!strvec) return; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global lvs_netlink_monitor_rcv_bufs_force specified", strvec_slot(strvec, 1)); return; } } global_data->lvs_netlink_monitor_rcv_bufs_force = res; } static void lvs_netlink_cmd_rcv_bufs_handler(const vector_t *strvec) { unsigned val; if (!strvec) return; val = get_netlink_rcv_bufs_size(strvec, "lvs_netlink_cmd"); if (val) global_data->lvs_netlink_cmd_rcv_bufs = val; } static void lvs_netlink_cmd_rcv_bufs_force_handler(const vector_t *strvec) { int res = true; if (!strvec) return; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global lvs_netlink_cmd_rcv_bufs_force specified", strvec_slot(strvec, 1)); return; } } global_data->lvs_netlink_cmd_rcv_bufs_force = res; } static void rs_init_notifies_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global rs_init_notifies specified", strvec_slot(strvec, 1)); return; } } global_data->rs_init_notifies = res; } static void no_checker_emails_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global no_checker_emails specified", strvec_slot(strvec, 1)); return; } } global_data->no_checker_emails = res; } #endif static void umask_handler(const vector_t *strvec) { long umask_long; mode_t umask_bits = 0; const char *mask = strvec_slot(strvec, 1); char *endptr; unsigned i; const char *p; if (umask_cmdline) { log_message(LOG_INFO, "umask command line option specified, ignoring config option"); return; } if (isdigit(mask[0])) { if (vector_size(strvec) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "%s parameter(s) to umask option", vector_size(strvec) == 1 ? "Missing" : "Extra"); return; } umask_long = strtol(mask, &endptr, 0); if (*endptr || umask_long < 0 || umask_long & ~(S_IRWXU | S_IRWXG | S_IRWXO)) { report_config_error(CONFIG_GENERAL_ERROR, "invalid umask value %s", mask); return; } umask_bits = umask_long & (S_IRWXU | S_IRWXG | S_IRWXO); } else { bool need_or = false; for (i = 1; i < vector_size(strvec); i++) { for (p = strvec_slot(strvec, i); *p; ) { if (need_or) { if (*p == '|') { need_or = false; p++; continue; } report_config_error(CONFIG_GENERAL_ERROR, "Invalid umask syntax %s", strvec_slot(strvec, i)); return; } if (!strncmp(p, "IRUSR", 5)) umask_bits |= S_IRUSR; else if (!strncmp(p, "IWUSR", 5)) umask_bits |= S_IWUSR; else if (!strncmp(p, "IXUSR", 5)) umask_bits |= S_IXUSR; else if (!strncmp(p, "IRWXU", 5)) umask_bits |= S_IRWXU; else if (!strncmp(p, "IRGRP", 5)) umask_bits |= S_IRGRP; else if (!strncmp(p, "IWGRP", 5)) umask_bits |= S_IWGRP; else if (!strncmp(p, "IXGRP", 5)) umask_bits |= S_IXGRP; else if (!strncmp(p, "IRWXG", 5)) umask_bits |= S_IRWXG; else if (!strncmp(p, "IROTH", 5)) umask_bits |= S_IROTH; else if (!strncmp(p, "IWOTH", 5)) umask_bits |= S_IWOTH; else if (!strncmp(p, "IXOTH", 5)) umask_bits |= S_IXOTH; else if (!strncmp(p, "IRWXO", 5)) umask_bits |= S_IRWXO; else { report_config_error(CONFIG_GENERAL_ERROR, "Unknown umask bit %s", p); return; } p += 5; need_or = true; } } if (!need_or) { report_config_error(CONFIG_GENERAL_ERROR, "umask missing bit value"); return; } } umask_val = umask_bits; umask(umask_bits); #ifdef _MEM_CHECK_ update_mem_check_log_perms(umask_bits); #endif #ifdef ENABLE_LOG_TO_FILE update_log_file_perms(umask_bits); #endif } #ifdef _WITH_VRRP_ static void vrrp_startup_delay_handler(const vector_t *strvec) { unsigned startup_delay; if (!read_decimal_unsigned_strvec(strvec, 1, &startup_delay, TIMER_HZ / 1000, UINT_MAX, TIMER_HZ_DIGITS, true)) report_config_error(CONFIG_GENERAL_ERROR, "vrrp_startup_delay '%s' is invalid", strvec_slot(strvec, 1)); else global_data->vrrp_startup_delay = startup_delay; if (global_data->vrrp_startup_delay >= 60 * TIMER_HZ) log_message(LOG_INFO, "The vrrp_startup_delay is very large - %s seconds", strvec_slot(strvec, 1)); } static void vrrp_log_unknown_vrids_handler(__attribute__((unused)) const vector_t *strvec) { global_data->log_unknown_vrids = true; } static void vrrp_owner_ignore_adverts_handler(__attribute__((unused)) const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global %s specified", strvec_slot(strvec, 0), strvec_slot(strvec, 1)); return; } } global_data->vrrp_owner_ignore_adverts = res; } #ifdef _HAVE_VRRP_VMAC_ static void vrrp_vmac_prefix_handler(const vector_t *strvec) { if (global_data->vmac_prefix) { report_config_error(CONFIG_GENERAL_ERROR, "vmac prefix has already been specified - ignoring %s", strvec_slot(strvec, 1)); return; } global_data->vmac_prefix = STRDUP(strvec_slot(strvec, 1)); } static void vrrp_vmac_addr_prefix_handler(const vector_t *strvec) { if (global_data->vmac_addr_prefix) { report_config_error(CONFIG_GENERAL_ERROR, "vmac_addr prefix has already been specified - ignoring %s", strvec_slot(strvec, 1)); return; } global_data->vmac_addr_prefix = STRDUP(strvec_slot(strvec, 1)); } #endif #endif static void random_seed_handler(const vector_t *strvec) { unsigned val; if (!read_unsigned_strvec(strvec, 1, &val, 0, UINT_MAX, false)) { report_config_error(CONFIG_GENERAL_ERROR, "random_seed %s invalid", strvec_slot(strvec, 1)); return; } set_random_seed(val); } #ifndef _ONE_PROCESS_DEBUG_ static void include_check_handler(const vector_t *strvec) { include_check_set(strvec); } static const char * set_dir(const char *dir_name) { char *save_dir = STRDUP(dir_name); size_t end; /* Remove a trailing / */ end = strlen(save_dir); if (end && save_dir[end-1] == '/') save_dir[end-1] = '\0'; return save_dir; } static void config_save_dir_handler(const vector_t *strvec) { struct stat statbuf; const char *dir_name = strvec_slot(strvec, 1); int ret; /* We are checking the specified path is a directory on a best * efforts basis; we don't have a problem if we later try * creating a file in the directory and that fails. */ /* coverity[fs_check_call] */ ret = stat(dir_name, &statbuf); if (!ret && statbuf.st_mode & S_IFDIR) { /* dir_name exists and is a directory */ config_save_dir = set_dir(dir_name); return; } if (prog_type != PROG_TYPE_PARENT) { /* If we are not the parent, then the directory should exist */ if (ret) report_config_error(CONFIG_GENERAL_ERROR, "Unable to find config_save_dir %s", dir_name); else report_config_error(CONFIG_GENERAL_ERROR, "config_save_dir %s is not a directory", dir_name); return; } if (ret && errno == ENOENT) { /* No matching entry exists - create the directory */ if (!mkdir(dir_name, S_IRWXU)) config_save_dir = set_dir(dir_name); else report_config_error(CONFIG_GENERAL_ERROR, "Unable to create config_save_dir %s (error %d - %m)", dir_name, errno); } else if (ret) report_config_error(CONFIG_GENERAL_ERROR, "config_save_dir %s error %d - %m", dir_name, errno); else report_config_error(CONFIG_GENERAL_ERROR, "config_save_dir %s exists and is not a directory", dir_name); } static void reload_check_config_handler(const vector_t *strvec) { if (vector_size(strvec) >= 2) { FREE_CONST_PTR(global_data->reload_check_config); global_data->reload_check_config = set_value(strvec); /* Check file can be written */ } else global_data->reload_check_config = STRDUP("/dev/null"); } static void reload_time_file_handler(const vector_t *strvec) { if (vector_size(strvec) != 2) { report_config_error(CONFIG_GENERAL_ERROR, "reload_time_file invalid"); return; } global_data->reload_time_file = STRDUP(strvec_slot(strvec, 1)); } static void reload_repeat_handler(__attribute__((unused)) const vector_t *strvec) { global_data->reload_repeat = true; } static void reload_file_handler(const vector_t *strvec) { if (global_data->reload_file && global_data->reload_file != DEFAULT_RELOAD_FILE) FREE_CONST_PTR(global_data->reload_file); if (vector_size(strvec) >= 2) global_data->reload_file = STRDUP(strvec_slot(strvec, 1)); else global_data->reload_file = DEFAULT_RELOAD_FILE; } #endif static void config_copy_directory_handler(const vector_t *strvec) { if (global_data->config_directory) { report_config_error(CONFIG_GENERAL_ERROR, "%s already specified - ignoring", strvec_slot(strvec, 0)); return; } if (vector_size(strvec) >= 2) { global_data->config_directory = STRDUP(strvec_slot(strvec, 1)); /* Copy the configuration read so far to the new location */ if (!reload && !__test_bit(CONFIG_TEST_BIT, &debug)) use_disk_copy_for_config(global_data->config_directory); } else report_config_error(CONFIG_GENERAL_ERROR, "%s missing directory name", strvec_slot(strvec, 0)); } static void data_use_instance_handler(const vector_t *strvec) { int res = true; if (vector_size(strvec) >= 2) { res = check_true_false(strvec_slot(strvec,1)); if (res < 0) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid value '%s' for global date_use_instance specified", strvec_slot(strvec, 1)); return; } } global_data->data_use_instance = res; } #ifdef _WITH_JSON_ static void 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, 0)); return; } if (!read_unsigned_strvec(strvec, 1, &version, JSON_VERSION_V1, JSON_VERSION_V2, true)) { report_config_error(CONFIG_GENERAL_ERROR, "Invalid JSON version"); return; } global_data->json_version = version; } #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 static void state_dump_file_handler(const vector_t *strvec) { if (vector_size(strvec) != 2 || !strvec_slot(strvec, 1)[0]) { report_config_error(CONFIG_GENERAL_ERROR, "%s requires a non-empty path", strvec_slot(strvec, 0)); return; } global_data->state_dump_file = STRDUP(strvec_slot(strvec, 1)); } static void stats_dump_file_handler(const vector_t *strvec) { if (vector_size(strvec) != 2 || !strvec_slot(strvec, 1)[0]) { report_config_error(CONFIG_GENERAL_ERROR, "%s requires a non-empty path", strvec_slot(strvec, 0)); return; } global_data->stats_dump_file = STRDUP(strvec_slot(strvec, 1)); } static void json_dump_file_handler(const vector_t *strvec) { if (vector_size(strvec) != 2 || !strvec_slot(strvec, 1)[0]) { report_config_error(CONFIG_GENERAL_ERROR, "%s requires a non-empty path", strvec_slot(strvec, 0)); return; } global_data->json_dump_file = STRDUP(strvec_slot(strvec, 1)); } void init_global_keywords(bool global_active) { /* global definitions mapping */ #ifdef _WITH_LINKBEAT_ install_keyword_root("linkbeat_use_polling", use_polling_handler, global_active, NULL); #endif install_keyword_root("net_namespace", &net_namespace_handler, global_active, NULL); install_keyword_root("net_namespace_ipvs", &net_namespace_ipvs_handler, global_active, NULL); install_keyword_root("namespace_with_ipsets", &namespace_ipsets_handler, global_active, NULL); install_keyword_root("use_pid_dir", &use_pid_dir_handler, global_active, NULL); install_keyword_root("instance", &instance_handler, global_active, NULL); install_keyword_root("child_wait_time", &child_wait_handler, global_active, NULL); install_keyword_root("global_defs", NULL, global_active, VPP &global_data); install_keyword("process_names", &process_names_handler); install_keyword("process_name", &process_name_handler); #ifdef _WITH_VRRP_ install_keyword("vrrp_process_name", &vrrp_process_name_handler); #endif #ifdef _WITH_LVS_ install_keyword("checker_process_name", &checker_process_name_handler); install_keyword("lvs_process_name", &lvs_process_name_handler); /* Deprecated since 12/07/20 */ #endif #ifdef _WITH_BFD_ install_keyword("bfd_process_name", &bfd_process_name_handler); #endif install_keyword("use_symlink_paths", &use_symlink_path_handler); install_keyword("router_id", &routerid_handler); install_keyword("notification_email_from", &emailfrom_handler); install_keyword("smtp_server", &smtpserver_handler); install_keyword("smtp_helo_name", &smtphelo_handler); install_keyword("smtp_connect_timeout", &smtpto_handler); install_keyword("notification_email", &email_handler); install_keyword("smtp_alert", &smtp_alert_handler); install_keyword_quoted("startup_script", &startup_script_handler); install_keyword("startup_script_timeout", &startup_script_timeout_handler); install_keyword_quoted("shutdown_script", &shutdown_script_handler); install_keyword("shutdown_script_timeout", &shutdown_script_timeout_handler); install_keyword("max_auto_priority", &max_auto_priority_handler); install_keyword("min_auto_priority_delay", &min_auto_priority_delay_handler); #ifdef _WITH_VRRP_ install_keyword("smtp_alert_vrrp", &smtp_alert_vrrp_handler); #endif #ifdef _WITH_LVS_ install_keyword("smtp_alert_checker", &smtp_alert_checker_handler); install_keyword("checker_log_all_failures", &checker_log_all_failures_handler); #endif #ifdef _WITH_VRRP_ install_keyword("dynamic_interfaces", &dynamic_interfaces_handler); install_keyword("no_email_faults", &no_email_faults_handler); install_keyword("default_interface", &default_interface_handler); install_keyword("disable_local_igmp", &disable_local_igmp_handler); install_keyword("v3_checksum_as_v2", &v3_checksum_as_v2); #endif #ifdef _WITH_LVS_ install_keyword("lvs_timeouts", &lvs_timeouts); install_keyword("lvs_flush", &lvs_flush_handler); install_keyword("lvs_flush_on_stop", &lvs_flush_on_stop_handler); install_keyword("lvs_flush_onstop", &lvs_flush_on_stop_handler); /* Deprecated after v2.1.5 */ #ifdef _WITH_VRRP_ install_keyword("lvs_sync_daemon", &lvs_syncd_handler); #endif #endif #ifdef _WITH_VRRP_ install_keyword("vrrp_mcast_group4", &vrrp_mcast_group4_handler); install_keyword("vrrp_mcast_group6", &vrrp_mcast_group6_handler); install_keyword("vrrp_garp_master_delay", &vrrp_garp_delay_handler); install_keyword("vrrp_garp_master_repeat", &vrrp_garp_rep_handler); install_keyword("vrrp_garp_master_refresh", &vrrp_garp_refresh_handler); install_keyword("vrrp_garp_master_refresh_repeat", &vrrp_garp_refresh_rep_handler); install_keyword("vrrp_garp_lower_prio_delay", &vrrp_garp_lower_prio_delay_handler); install_keyword("vrrp_garp_lower_prio_repeat", &vrrp_garp_lower_prio_rep_handler); install_keyword("vrrp_down_timer_adverts", &vrrp_down_timer_adverts_handler); install_keyword("vrrp_garp_interval", &vrrp_garp_interval_handler); install_keyword("vrrp_gna_interval", &vrrp_gna_interval_handler); install_keyword("vrrp_min_garp", &vrrp_min_garp_handler); #ifdef _HAVE_VRRP_VMAC_ install_keyword("vrrp_garp_extra_if", &vrrp_vmac_garp_extra_if_handler); install_keyword("vrrp_vmac_garp_intvl", &vrrp_vmac_garp_extra_if_handler); /* Deprecated after v2.2.2 - incorrect keyword in commit 3dcd13c */ #endif install_keyword("vrrp_lower_prio_no_advert", &vrrp_lower_prio_no_advert_handler); install_keyword("vrrp_higher_prio_send_advert", &vrrp_higher_prio_send_advert_handler); install_keyword("vrrp_version", &vrrp_version_handler); #if defined _WITH_IPTABLES_ || defined _WITH_NFTABLES_ /* We keep the vrrp_iptables command for legacy reasons, and * will use nftables instead if it is specified and keepalived * is not built with iptables support. */ install_keyword("vrrp_iptables", &vrrp_iptables_handler); #ifdef _HAVE_LIBIPSET_ install_keyword("vrrp_ipsets", &vrrp_ipsets_handler); #endif #endif #ifdef _WITH_NFTABLES_ install_keyword("nftables", &vrrp_nftables_handler); install_keyword("nftables_priority", &vrrp_nftables_priority_handler); install_keyword("nftables_ifindex", &vrrp_nftables_ifindex_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); install_keyword("vrrp_strict", &vrrp_strict_handler); install_keyword("vrrp_priority", &vrrp_prio_handler); install_keyword("vrrp_no_swap", &vrrp_no_swap_handler); install_keyword("vrrp_rt_priority", &vrrp_rt_priority_handler); 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); #ifdef _WITH_VRRP_ install_keyword("vrrp_notify_fifo", &vrrp_notify_fifo); install_keyword_quoted("vrrp_notify_fifo_script", &vrrp_notify_fifo_script); install_keyword("vrrp_notify_priority_changes", &vrrp_notify_priority_changes); install_keyword("fifo_write_vrrp_states_on_reload", &fifo_write_vrrp_states_on_reload); #endif #ifdef _WITH_LVS_ install_keyword("lvs_notify_fifo", &lvs_notify_fifo); install_keyword_quoted("lvs_notify_fifo_script", &lvs_notify_fifo_script); install_keyword("checker_priority", &checker_prio_handler); install_keyword("checker_no_swap", &checker_no_swap_handler); install_keyword("checker_rt_priority", &checker_rt_priority_handler); install_keyword("checker_cpu_affinity", &checker_cpu_affinity_handler); install_keyword("checker_rlimit_rttime", &checker_rt_rlimit_handler); install_keyword("checker_rlimit_rtime", &checker_rt_rlimit_handler); /* Deprecated 02/02/2020 */ #endif #ifdef _WITH_BFD_ install_keyword("bfd_priority", &bfd_prio_handler); install_keyword("bfd_no_swap", &bfd_no_swap_handler); install_keyword("bfd_rt_priority", &bfd_rt_priority_handler); install_keyword("bfd_cpu_affinity", &bfd_cpu_affinity_handler); install_keyword("bfd_rlimit_rttime", &bfd_rt_rlimit_handler); install_keyword("bfd_rlimit_rtime", &bfd_rt_rlimit_handler); /* Deprecated 02/02/2020 */ #endif #ifdef _WITH_SNMP_ install_keyword("snmp_socket", &snmp_socket_handler); install_keyword("enable_traps", &trap_handler); #ifdef _WITH_SNMP_VRRP_ install_keyword("enable_snmp_vrrp", &snmp_vrrp_handler); install_keyword("enable_snmp_keepalived", &snmp_vrrp_handler); /* Deprecated v2.0.0 */ #endif #ifdef _WITH_SNMP_RFC_ install_keyword("enable_snmp_rfc", &snmp_rfc_handler); #endif #ifdef _WITH_SNMP_RFCV2_ install_keyword("enable_snmp_rfcv2", &snmp_rfcv2_handler); #endif #ifdef _WITH_SNMP_RFCV3_ install_keyword("enable_snmp_rfcv3", &snmp_rfcv3_handler); #endif #ifdef _WITH_SNMP_CHECKER_ install_keyword("enable_snmp_checker", &snmp_checker_handler); install_keyword("snmp_vs_stats_update_interval", &snmp_vs_stats_update_interval_handler); install_keyword("snmp_rs_stats_update_interval", &snmp_rs_stats_update_interval_handler); #endif #endif #ifdef _WITH_DBUS_ install_keyword("enable_dbus", &enable_dbus_handler); install_keyword("dbus_service_name", &dbus_service_name_handler); install_keyword("dbus_no_interface_name", &dbus_no_interface_name_handler); #endif install_keyword("script_user", &script_user_handler); install_keyword("enable_script_security", &script_security_handler); #ifdef _WITH_VRRP_ install_keyword("vrrp_netlink_cmd_rcv_bufs", &vrrp_netlink_cmd_rcv_bufs_handler); install_keyword("vrrp_netlink_cmd_rcv_bufs_force", &vrrp_netlink_cmd_rcv_bufs_force_handler); install_keyword("vrrp_netlink_monitor_rcv_bufs", &vrrp_netlink_monitor_rcv_bufs_handler); install_keyword("vrrp_netlink_monitor_rcv_bufs_force", &vrrp_netlink_monitor_rcv_bufs_force_handler); #ifdef _WITH_TRACK_PROCESS_ install_keyword("process_monitor_rcv_bufs", &process_monitor_rcv_bufs_handler); install_keyword("process_monitor_rcv_bufs_force", &process_monitor_rcv_bufs_force_handler); #endif #endif #ifdef _WITH_LVS_ install_keyword("lvs_netlink_cmd_rcv_bufs", &lvs_netlink_cmd_rcv_bufs_handler); install_keyword("lvs_netlink_cmd_rcv_bufs_force", &lvs_netlink_cmd_rcv_bufs_force_handler); install_keyword("lvs_netlink_monitor_rcv_bufs", &lvs_netlink_monitor_rcv_bufs_handler); install_keyword("lvs_netlink_monitor_rcv_bufs_force", &lvs_netlink_monitor_rcv_bufs_force_handler); install_keyword("rs_init_notifies", &rs_init_notifies_handler); install_keyword("no_checker_emails", &no_checker_emails_handler); #endif #ifdef _WITH_VRRP_ install_keyword("vrrp_rx_bufs_policy", &vrrp_rx_bufs_policy_handler); install_keyword("vrrp_rx_bufs_multiplier", &vrrp_rx_bufs_multiplier_handler); install_keyword("vrrp_startup_delay", &vrrp_startup_delay_handler); install_keyword("log_unknown_vrids", &vrrp_log_unknown_vrids_handler); install_keyword("vrrp_owner_ignore_adverts", &vrrp_owner_ignore_adverts_handler); #ifdef _HAVE_VRRP_VMAC_ install_keyword("vmac_prefix", &vrrp_vmac_prefix_handler); install_keyword("vmac_addr_prefix", &vrrp_vmac_addr_prefix_handler); #endif #endif install_keyword("umask", &umask_handler); install_keyword("random_seed", &random_seed_handler); #ifndef _ONE_PROCESS_DEBUG_ install_keyword("reload_check_config", &reload_check_config_handler); install_keyword("reload_time_file", &reload_time_file_handler); install_keyword("reload_repeat", &reload_repeat_handler); install_keyword("reload_file", &reload_file_handler); install_keyword("include_check", &include_check_handler); install_keyword("config_save_dir", &config_save_dir_handler); #endif install_keyword("tmp_config_directory", &config_copy_directory_handler); install_keyword("data_use_instance", &data_use_instance_handler); #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 install_keyword("state_dump_file", &state_dump_file_handler); install_keyword("stats_dump_file", &stats_dump_file_handler); install_keyword("json_dump_file", &json_dump_file_handler); } keepalived-2.3.3/keepalived/core/main.c0000664000175000017500000023111714745724073013466 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Main program structure. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "global_data.h" #include "daemon.h" #ifndef _ONE_PROCESS_DEBUG_ #include "config_notify.h" #endif #include "git-commit.h" #include "utils.h" #include "signals.h" #include "pidfile.h" #include "bitops.h" #include "logger.h" #include "parser.h" #include "notify.h" #include "track_file.h" #ifdef _WITH_LVS_ #include "check_genhash.h" #include "check_parser.h" #include "check_daemon.h" #endif #ifdef _WITH_VRRP_ #include "vrrp_daemon.h" #include "vrrp_parser.h" #include "vrrp_if.h" #ifdef _WITH_TRACK_PROCESS_ #include "track_process.h" #endif #ifdef _WITH_JSON_ #include "vrrp_json.h" #endif #ifdef _WITH_NFTABLES_ #include "vrrp_nftables.h" #endif #endif #ifdef _WITH_BFD_ #include "bfd_daemon.h" #include "bfd_parser.h" #endif #include "global_parser.h" #include "namespaces.h" #include "scheduler.h" #include "keepalived_netlink.h" #if defined THREAD_DUMP || defined _EPOLL_DEBUG_ || defined _EPOLL_THREAD_DUMP_ || defined _SCRIPT_DEBUG_ #include "scheduler.h" #endif #include "process.h" #ifdef _TIMER_CHECK_ #include "timer.h" #endif #if defined _SMTP_ALERT_DEBUG_ || defined _SMTP_CONNECT_DEBUG_ #include "smtp.h" #endif #if defined _REGEX_DEBUG_ || defined _WITH_REGEX_TIMERS_ #include "check_http.h" #endif #if defined _NETWORK_TIMESTAMP_ || defined _CHECKSUM_DEBUG_ #include "vrrp.h" #endif #if defined _TSM_DEBUG_ || defined _RECVMSG_DEBUG_ #include "vrrp_scheduler.h" #endif #if defined _PARSER_DEBUG_ || defined _DUMP_KEYWORDS_ #include "parser.h" #endif #ifdef _CHECKER_DEBUG_ #include "check_api.h" #endif #ifdef _MEM_ERR_DEBUG_ #include "memory.h" #endif #ifndef _ONE_PROCESS_DEBUG_ #include "reload_monitor.h" #endif #ifdef _USE_SYSTEMD_NOTIFY_ #include "systemd.h" #endif #ifdef _WITH_SANITIZER_ #include "sanitizer.h" #endif #include "warnings.h" #define CHILD_WAIT_SECS 5 /* Structure used for handling termination of children */ struct child_term { pid_t * const pid_p; const char * const name; const char * const short_name; }; #ifndef _ONE_PROCESS_DEBUG_ static const struct child_term children_term[] = { #ifdef _WITH_VRRP_ { &vrrp_child, PROG_VRRP, "vrrp" }, #endif #ifdef _WITH_LVS_ { &checkers_child, PROG_CHECK, "checker" }, #endif #ifdef _WITH_BFD_ { &bfd_child, PROG_BFD, "bfd" }, #endif }; #define NUM_CHILD_TERM (sizeof children_term / sizeof children_term[0]) #endif /* global var */ const char *version_string = VERSION_STRING; /* keepalived version */ const char *conf_file; /* Configuration file */ bool reload; /* Set during a reload */ struct pidfile main_pidfile = { .fd = -1 }; /* overrule default pidfile */ #ifdef _WITH_LVS_ pid_t checkers_child; /* Healthcheckers child process ID */ struct pidfile checkers_pidfile = { .fd = -1 }; /* overrule default pidfile */ #endif #ifdef _WITH_VRRP_ pid_t vrrp_child; /* VRRP child process ID */ struct pidfile vrrp_pidfile = { .fd = -1 }; /* overrule default pidfile */ #endif #ifdef _WITH_BFD_ pid_t bfd_child; /* BFD child process ID */ struct pidfile bfd_pidfile = { .fd = -1 }; /* overrule default pidfile */ #endif unsigned long daemon_mode; /* VRRP/CHECK/BFD subsystem selection */ #ifdef _WITH_SNMP_ bool snmp_option; /* Enable SNMP support */ const char *snmp_socket; /* Socket to use for SNMP agent */ #endif static const char *syslog_ident; /* syslog ident if not default */ bool use_pid_dir; /* Put pid files in /run/keepalived or @localstatedir@/run/keepalived */ bool children_started; /* Set once children have been run first time */ unsigned os_major; /* Kernel version */ unsigned os_minor; unsigned os_release; char *hostname; /* Initial part of hostname */ static char *override_namespace; /* If namespace specified on command line */ unsigned child_wait_time = CHILD_WAIT_SECS; /* Time to wait for children to exit */ /* Log facility table */ static struct { int facility; } LOG_FACILITY[] = { {LOG_LOCAL0}, {LOG_LOCAL1}, {LOG_LOCAL2}, {LOG_LOCAL3}, {LOG_LOCAL4}, {LOG_LOCAL5}, {LOG_LOCAL6}, {LOG_LOCAL7} }; #define LOG_FACILITY_MAX ((sizeof(LOG_FACILITY) / sizeof(LOG_FACILITY[0])) - 1) static struct { const char *name; int facility; } facility_names[] = { { "daemon", LOG_DAEMON }, { "user", LOG_USER } }; /* umask settings */ bool umask_cmdline; /* Reload control */ unsigned num_reloading; /* Control producing core dumps */ static bool set_core_dump_pattern = false; static bool create_core_dump = false; static const char *core_dump_pattern = "core"; static char *orig_core_dump_pattern = NULL; /* Signal handling */ bool ignore_sigint = false; /* debug flags */ #if defined _TIMER_CHECK_ || \ defined _SMTP_ALERT_DEBUG_ || \ defined _SMTP_CONNECT_DEBUG_ || \ defined _EPOLL_DEBUG_ || \ defined _EPOLL_THREAD_DUMP_ || \ defined _REGEX_DEBUG_ || \ defined _WITH_REGEX_TIMERS_ || \ defined _TSM_DEBUG_ || \ defined _VRRP_FD_DEBUG_ || \ defined _NETLINK_TIMERS_ || \ defined _NETWORK_TIMESTAMP_ || \ defined _CHECKSUM_DEBUG_ || \ defined _TRACK_PROCESS_DEBUG_ || \ defined _PARSER_DEBUG_ || \ defined _DUMP_KEYWORDS_ || \ defined _CHECKER_DEBUG_ || \ defined _MEM_ERR_DEBUG_ || \ defined _RECVMSG_DEBUG_ || \ defined _EINTR_DEBUG_ || \ defined _SCRIPT_DEBUG_ #define WITH_DEBUG_OPTIONS 1 #endif #ifdef _TIMER_CHECK_ static char timer_debug; #endif #ifdef _SMTP_ALERT_DEBUG_ static char smtp_debug; #endif #ifdef _SMTP_CONNECT_DEBUG_ static char smtp_connect_debug; #endif #ifdef _EPOLL_DEBUG_ static char epoll_debug; #endif #ifdef _EPOLL_THREAD_DUMP_ static char epoll_thread_debug; #endif #ifdef _REGEX_DEBUG_ static char regex_debug; #endif #ifdef _WITH_REGEX_TIMERS_ static char regex_timers; #endif #ifdef _TSM_DEBUG_ static char tsm_debug; #endif #ifdef _VRRP_FD_DEBUG_ static char vrrp_fd_debug; #endif #ifdef _NETLINK_TIMERS_ static char netlink_timer_debug; #endif #ifdef _NETWORK_TIMESTAMP_ static char network_timestamp_debug; #endif #ifdef _CHECKSUM_DEBUG_ static char checksum_debug; #endif #ifdef _TRACK_PROCESS_DEBUG_ static char track_process_debug; static char track_process_debug_detail; #endif #ifdef _PARSER_DEBUG_ static char parser_debug; #endif #ifdef _CHECKER_DEBUG_ static char checker_debug; #endif #ifdef _MEM_ERR_DEBUG_ static char mem_err_debug; #endif #ifdef _RECVMSG_DEBUG_ static char recvmsg_debug; static char recvmsg_debug_dump; #endif #ifdef _EINTR_DEBUG_ static char eintr_debug; #endif #ifdef _SCRIPT_DEBUG_ static char script_debug; #endif #ifdef _DUMP_KEYWORDS_ static char dump_keywords; #endif void free_parent_mallocs_startup(bool am_child) { if (am_child) { free_dirname(); #ifdef _MEM_CHECK_LOG_ free(no_const_char_p(syslog_ident)); /* malloc'd in make_syslog_ident */ #else FREE_CONST_PTR(syslog_ident); #endif syslog_ident = NULL; FREE_PTR(orig_core_dump_pattern); free_notify_script(&global_data->startup_script); free_notify_script(&global_data->shutdown_script); } } void free_parent_mallocs_exit(void) { FREE_CONST_PTR(config_id); #ifdef _REPRODUCIBLE_BUILD_ FREE_CONST_PTR(config_opts); #endif } const char * make_syslog_ident(const char* name) { size_t ident_len = strlen(name) + 1; char *ident; if (global_data->network_namespace) ident_len += strlen(global_data->network_namespace) + 1; if (global_data->instance_name) ident_len += strlen(global_data->instance_name) + 1; /* If we are writing MALLOC/FREE info to the log, we have * trouble FREEing the syslog_ident */ #ifdef _MEM_CHECK_LOG_ ident = malloc(ident_len); /* Required to stop loop */ #else ident = MALLOC(ident_len); #endif if (!ident) return NULL; strcpy(ident, name); if (global_data->network_namespace) { strcat(ident, "_"); strcat(ident, global_data->network_namespace); } if (global_data->instance_name) { strcat(ident, "_"); strcat(ident, global_data->instance_name); } return ident; } #ifdef _WITH_VRRP_ bool __attribute__ ((pure)) running_vrrp(void) { return (__test_bit(DAEMON_VRRP, &daemon_mode) && (global_data->have_vrrp_config || __test_bit(RUN_ALL_CHILDREN, &daemon_mode))); } #endif #ifdef _WITH_LVS_ bool __attribute__ ((pure)) running_checker(void) { return (__test_bit(DAEMON_CHECKERS, &daemon_mode) && (global_data->have_checker_config || __test_bit(RUN_ALL_CHILDREN, &daemon_mode))); } #endif #ifdef _WITH_BFD_ static bool __attribute__ ((pure)) running_bfd(void) { return (__test_bit(DAEMON_BFD, &daemon_mode) && (global_data->have_bfd_config || __test_bit(RUN_ALL_CHILDREN, &daemon_mode))); } #endif static char const * find_keepalived_child_name(pid_t pid) { #ifdef _WITH_LVS_ if (pid == checkers_child) return PROG_CHECK; #endif #ifdef _WITH_VRRP_ if (pid == vrrp_child) return PROG_VRRP; #endif #ifdef _WITH_BFD_ if (pid == bfd_child) return PROG_BFD; #endif return NULL; } static const vector_t * global_init_keywords(void) { /* global definitions mapping */ init_global_keywords(true); #ifdef _WITH_VRRP_ init_vrrp_keywords(false); #endif #ifdef _WITH_LVS_ init_check_keywords(false); #endif #ifdef _WITH_BFD_ init_bfd_keywords(false); #endif #if defined _WITH_VRRP_ || defined _WITH_LVS_ add_track_file_keywords(false); #endif return keywords; } #ifndef _ONE_PROCESS_DEBUG_ static void create_reload_file(void) { int fd; if (!global_data->reload_file || __test_bit(CONFIG_TEST_BIT, &debug)) return; /* We want to create the reloading file with permissions rw-r--r-- */ if (umask_val & (S_IRGRP | S_IROTH)) umask(umask_val & ~(S_IRGRP | S_IROTH)); if ((fd = creat(global_data->reload_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) != -1) close(fd); else log_message(LOG_INFO, "Failed to create reload file (%s) %d - %m", global_data->reload_file, errno); /* Restore the default umask */ if (umask_val & (S_IRGRP | S_IROTH)) umask(umask_val); } static void remove_reload_file(void) { if (global_data->reload_file && !__test_bit(CONFIG_TEST_BIT, &debug)) unlink(global_data->reload_file); } #endif static void read_config_file(bool write_config_copy) { #ifndef _ONE_PROCESS_DEBUG_ if (write_config_copy) create_reload_file(); #else write_config_copy = false; #endif init_data(conf_file, global_init_keywords, write_config_copy); #ifndef _ONE_PROCESS_DEBUG_ if (write_config_copy) remove_reload_file(); #endif } /* Daemon stop sequence */ static void stop_keepalived(void) { #ifndef _ONE_PROCESS_DEBUG_ /* Just cleanup memory & exit */ thread_destroy_master(master); #ifdef _WITH_VRRP_ if (__test_bit(DAEMON_VRRP, &daemon_mode)) pidfile_close(&vrrp_pidfile, true); #endif #ifdef _WITH_LVS_ if (__test_bit(DAEMON_CHECKERS, &daemon_mode)) pidfile_close(&checkers_pidfile, true); #endif #ifdef _WITH_BFD_ if (__test_bit(DAEMON_BFD, &daemon_mode)) pidfile_close(&bfd_pidfile, true); #endif pidfile_rm(&main_pidfile); #endif } /* Daemon init sequence */ static void start_keepalived(__attribute__((unused)) thread_ref_t thread) { bool have_child = false; /* Although we use prctl to set PDEATHSIG, there are windows when it * is not set, i.e. before it is first executed after a fork, and also * after set(e)[ug]id() calls before PDEATHSIG can be reinstated. */ main_pid = getpid(); /* We want to ensure that any children of child process don't miss the * termination of their immediate parent. */ prctl(PR_SET_CHILD_SUBREAPER, 1); #ifdef _WITH_BFD_ /* must be opened before vrrp and bfd start */ if (!open_bfd_pipes()) { thread_add_terminate_event(thread->master); return; } #endif #ifdef _WITH_LVS_ /* start healthchecker child */ if (running_checker()) { start_check_child(); have_child = true; num_reloading++; } else pidfile_rm(&checkers_pidfile); #endif #ifdef _WITH_VRRP_ /* start vrrp child */ if (running_vrrp()) { start_vrrp_child(); have_child = true; num_reloading++; } else pidfile_rm(&vrrp_pidfile); #endif #ifdef _WITH_BFD_ /* start bfd child */ if (running_bfd()) { start_bfd_child(); have_child = true; num_reloading++; } else pidfile_rm(&bfd_pidfile); #endif children_started = true; #ifndef _ONE_PROCESS_DEBUG_ /* Do we have a reload file to monitor */ if (global_data->reload_time_file) start_reload_monitor(); #endif if (!have_child) log_message(LOG_INFO, "Warning - keepalived has no configuration to run"); } static bool handle_child_timeout(thread_ref_t thread, const char *type) { pid_t pid; int sig_num; void *next_arg; unsigned timeout = 0; pid = THREAD_CHILD_PID(thread); if (thread->arg == (void *)0) { next_arg = (void *)1; sig_num = SIGTERM; timeout = 2; log_message(LOG_INFO, "%s timed out", type); } else if (thread->arg == (void *)1) { next_arg = (void *)2; sig_num = SIGKILL; timeout = 2; } else if (thread->arg == (void *)2) { log_message(LOG_INFO, "%s (PID %d) failed to terminate after kill", type, pid); next_arg = (void *)3; sig_num = SIGKILL; timeout = 10; /* Give it longer to terminate */ } else if (thread->arg == (void *)3) { /* We give up trying to kill the script */ return true; } if (timeout) { /* If kill returns an error, we can't kill the process since either the process has terminated, * or we don't have permission. If we can't kill it, there is no point trying again. */ if (kill(-pid, sig_num)) { if (errno == ESRCH) { /* The process does not exist, and we should * have reaped its exit status, otherwise it * would exist as a zombie process. */ log_message(LOG_INFO, "%s (PID %d) lost", type, pid); timeout = 0; } else { log_message(LOG_INFO, "kill -%d of %s (%d) with new state %p failed with errno %d", sig_num, type, pid, next_arg, errno); timeout = 1000; } } } else { log_message(LOG_INFO, "%s %d timeout with unknown script state %p", type, pid, thread->arg); next_arg = thread->arg; timeout = 10; /* We need some timeout */ } if (timeout) thread_add_child(thread->master, thread->func, next_arg, pid, timeout * TIMER_HZ); return false; } static bool startup_shutdown_script_completed(thread_ref_t thread, bool startup) { const char *type = startup ? "startup script" : "shutdown script"; int wait_status; pid_t pid; if (thread->type == THREAD_CHILD_TIMEOUT) return handle_child_timeout(thread, type); wait_status = THREAD_CHILD_STATUS(thread); if (WIFEXITED(wait_status)) { unsigned status = WEXITSTATUS(wait_status); if (status) log_message(LOG_INFO, "%s script failed, status %u", type, status); else if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "%s script succeeded", type); } else if (WIFSIGNALED(wait_status)) { if (thread->arg == (void *)1 && WTERMSIG(wait_status) == SIGTERM) { /* The script terminated due to a SIGTERM, and we sent it a SIGTERM to * terminate the process. Now make sure any children it created have * died too. */ pid = THREAD_CHILD_PID(thread); kill(-pid, SIGKILL); } } return true; } static void startup_script_completed(thread_ref_t thread) { if (startup_shutdown_script_completed(thread, true)) thread_add_event(thread->master, start_keepalived, NULL, 0); } static void shutdown_script_completed(thread_ref_t thread) { if (startup_shutdown_script_completed(thread, false)) thread_add_terminate_event(thread->master); } static void run_startup_script(thread_ref_t thread) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Running startup script %s", global_data->startup_script->args[0]); if (system_call_script(thread->master, startup_script_completed, NULL, global_data->startup_script_timeout * TIMER_HZ, global_data->startup_script) == -1) log_message(LOG_INFO, "Call of startup script %s failed", global_data->startup_script->args[0]); } static void validate_config(void) { #ifdef _WITH_VRRP_ kernel_netlink_read_interfaces(); #endif #ifdef _WITH_LVS_ /* validate healthchecker config */ #ifndef _ONE_PROCESS_DEBUG_ prog_type = PROG_TYPE_CHECKER; #endif check_validate_config(); #endif #ifdef _WITH_VRRP_ /* validate vrrp config */ #ifndef _ONE_PROCESS_DEBUG_ prog_type = PROG_TYPE_VRRP; #endif vrrp_validate_config(); #endif #ifdef _WITH_BFD_ /* validate bfd config */ #ifndef _ONE_PROCESS_DEBUG_ prog_type = PROG_TYPE_BFD; #endif bfd_validate_config(); #endif } static void config_test_exit(void) { config_err_t config_err = get_config_status(); switch (config_err) { case CONFIG_OK: exit(KEEPALIVED_CHK_EXIT_OK); case CONFIG_FILE_NOT_FOUND: case CONFIG_BAD_IF: case CONFIG_FATAL: exit(KEEPALIVED_CHK_EXIT_CONFIG); case CONFIG_SECURITY_ERROR: exit(KEEPALIVED_CHK_EXIT_CONFIG_TEST_SECURITY); default: exit(KEEPALIVED_CHK_EXIT_CONFIG_TEST); } } static unsigned check_start_stop_script_secure(notify_script_t **script, magic_t magic) { unsigned flags; flags = check_script_secure(*script, magic); /* Mark not to run if needs inhibiting */ if (flags & (SC_INHIBIT | SC_NOTFOUND) || !(flags & (SC_EXECUTABLE | SC_SYSTEM))) free_notify_script(script); return flags; } #ifndef _ONE_PROCESS_DEBUG_ static bool reload_config(void) { bool unsupported_change = false; log_message(LOG_INFO, "Reloading ..."); if (global_data->reload_time_file) stop_reload_monitor(); /* Clear any config errors from previous loads */ clear_config_status(); /* Make sure there isn't an attempt to change the network namespace or instance name */ old_global_data = global_data; global_data = NULL; global_data = alloc_global_data(); /* If reload_check_config the process checking the config will read the config files, * otherwise this process needs to. */ read_config_file(!old_global_data->reload_check_config); init_global_data(global_data, old_global_data, false); if (override_namespace) { FREE_CONST_PTR(global_data->network_namespace); global_data->network_namespace = STRDUP(override_namespace); } if (!!old_global_data->network_namespace != !!global_data->network_namespace || (global_data->network_namespace && strcmp(old_global_data->network_namespace, global_data->network_namespace))) { log_message(LOG_INFO, "Cannot change network namespace at a reload - please restart %s", PACKAGE); unsupported_change = true; } #ifdef _WITH_LVS_ if (!!old_global_data->network_namespace_ipvs != !!global_data->network_namespace_ipvs || (global_data->network_namespace_ipvs && strcmp(old_global_data->network_namespace_ipvs, global_data->network_namespace_ipvs))) { log_message(LOG_INFO, "Cannot change IPVS network namespace at a reload - please restart %s", PACKAGE); unsupported_change = true; } #endif if (!!old_global_data->instance_name != !!global_data->instance_name || (global_data->instance_name && strcmp(old_global_data->instance_name, global_data->instance_name))) { log_message(LOG_INFO, "Cannot change instance name at a reload - please restart %s", PACKAGE); unsupported_change = true; } #ifdef _WITH_NFTABLES_ #ifdef _WITH_VRRP_ if (!!old_global_data->vrrp_nf_table_name != !!global_data->vrrp_nf_table_name || (global_data->vrrp_nf_table_name && strcmp(old_global_data->vrrp_nf_table_name, global_data->vrrp_nf_table_name))) { log_message(LOG_INFO, "Cannot change nftables table name at a reload - please restart %s", PACKAGE); unsupported_change = true; } #endif #ifdef _WITH_LVS_ if (!!old_global_data->ipvs_nf_table_name != !!global_data->ipvs_nf_table_name || (global_data->ipvs_nf_table_name && strcmp(old_global_data->ipvs_nf_table_name, global_data->ipvs_nf_table_name))) { log_message(LOG_INFO, "Cannot change IPVS nftables table name at a reload - please restart %s", PACKAGE); unsupported_change = true; } #endif #endif if (!!old_global_data->config_directory != !!global_data->config_directory || (global_data->config_directory && strcmp(old_global_data->config_directory, global_data->config_directory))) { log_message(LOG_INFO, "Cannot change config_directory at a reload - please restart %s", PACKAGE); unsupported_change = true; } #ifdef _WITH_VRRP_ if (old_global_data->disable_local_igmp != global_data->disable_local_igmp) { log_message(LOG_INFO, "Cannot change disable_local_igmp at a reload - please restart %s", PACKAGE); unsupported_change = true; } #endif if (unsupported_change) { /* We cannot reload the configuration, so continue with the old config */ free_global_data(&global_data); global_data = old_global_data; } else { /* Update process name if necessary */ if (!global_data->process_name != !old_global_data->process_name || (global_data->process_name && strcmp(global_data->process_name, old_global_data->process_name))) set_process_name(global_data->process_name); free_global_data(&old_global_data); } /* There is no point checking the script security of the * startup script, since we won't run it after a reload. */ if (global_data->shutdown_script) { magic_t magic; unsigned script_flags; magic = ka_magic_open(); script_flags = check_start_stop_script_secure(&global_data->shutdown_script, magic); if (magic) ka_magic_close(magic); if (!script_security && script_flags & SC_ISSCRIPT) { report_config_error(CONFIG_SECURITY_ERROR, "SECURITY VIOLATION - start/shutdown scripts are being executed but script_security not enabled.%s", script_flags & SC_INSECURE ? " There are insecure scripts." : ""); } } if (global_data->reload_time_file) start_reload_monitor(); return !unsupported_change; } static void print_parent_data(__attribute__((unused)) thread_ref_t thread) { FILE *fp; log_message(LOG_INFO, "Printing parent data for process(%d) on signal", getpid()); fp = open_dump_file("_parent"); if (!fp) return; dump_global_data(fp, global_data); fclose(fp); } void reinitialise_global_vars(void) { reset_default_script_user(); } /* SIGHUP/USR1/USR2/STATS_CLEAR handler */ static void propagate_signal(__attribute__((unused)) void *v, int sig) { /* Signal child processes */ #ifdef _WITH_VRRP_ if (vrrp_child > 0) kill(vrrp_child, sig); else if (sig == SIGHUP && running_vrrp()) start_vrrp_child(); #endif /* Only the VRRP process consumes SIGUSR2 and SIGJSON */ if (sig == SIGUSR2 || sig == SIGSTATS_CLEAR) return; #ifdef _WITH_JSON_ if (sig == SIGJSON) return; #endif #ifdef _WITH_LVS_ if (checkers_child > 0) kill(checkers_child, sig); else if (running_checker()) start_check_child(); #endif #ifdef _WITH_BFD_ if (bfd_child > 0) kill(bfd_child, sig); else if (running_bfd()) start_bfd_child(); #endif if (sig == SIGUSR1) thread_add_event(master, print_parent_data, NULL, 0); } static void do_reload(void) { reinitialise_global_vars(); if (!reload_config()) return; #ifdef _USE_SYSTEMD_NOTIFY_ systemd_notify_reloading(); #endif propagate_signal(NULL, SIGHUP); #ifdef _WITH_VRRP_ if (vrrp_child > 0) num_reloading++; #endif #ifdef _WITH_LVS_ if (checkers_child > 0) num_reloading++; #endif #ifdef _WITH_BFD_ if (bfd_child > 0) num_reloading++; #endif } static void reload_check_child_thread(thread_ref_t thread) { if (thread->type == THREAD_CHILD_TIMEOUT) { handle_child_timeout(thread, "config check"); return; } /* The config files have been read now */ remove_reload_file(); if (WIFEXITED(thread->u.c.status)) { if (WEXITSTATUS(thread->u.c.status)) { log_message(LOG_INFO, "New config failed validation, see %s for details", global_data->reload_check_config); return; } do_reload(); } else report_child_status(thread->u.c.status, thread->u.c.pid, "reload_check"); } static void start_validate_reload_conf_child(void) { notify_script_t script = { .path = NULL }; int i; int ret; int argc; const char **argv; char * const *sav_argv; char *config_test_str; char *config_fd_str = NULL; int fd; int len; char exe_buf[128]; struct stat sb; exe_buf[sizeof(exe_buf) - 1] = '\0'; ret = readlink("/proc/self/exe", exe_buf, sizeof(exe_buf)); if (ret == -1) { /* How can this happen? What can we do? */ log_message(LOG_INFO, "readlink(\"/proc/self/exe\" failed - errno %d - config-test aborted", errno); return; } else if (ret == sizeof(exe_buf)) strcpy(exe_buf, "/proc/self/exe"); else { exe_buf[ret] = '\0'; len = strlen(exe_buf); /* If keepalived has been recompiled, the original file will * be marked as deleted, but we can use the new one. */ if (len > 10 && !strcmp(exe_buf + len - 10, " (deleted)")) exe_buf[len - 10] = '\0'; } /* Inherits the original parameters and adds new parameters "--config-test and --config-fd" */ sav_argv = get_cmd_line_options(&argc); argv = MALLOC((argc + 3) * sizeof(char *)); argv[0] = exe_buf; /* copy old parameters */ for (i = 1; i < argc; i++) argv[i] = sav_argv[i]; /* add --config-test */ config_test_str = MALLOC(14 + strlen(global_data->reload_check_config) + 1); strcpy(config_test_str, "--config-test="); strcat(config_test_str, global_data->reload_check_config); argv[argc++] = config_test_str; /* add --config-fd */ if ((fd = get_config_fd()) != -1) { len = 13 + 6 + 1; /* --config-fd=XXXXXX */ config_fd_str = MALLOC(len); snprintf(config_fd_str, len, "--config-fd=%d", fd); argv[argc++] = config_fd_str; /* Allow fd to be inherited by exec'd process */ if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) & ~FD_CLOEXEC) == -1) log_message(LOG_INFO, "fcntl() on config-test fd failed - errno %d", errno); } argv[argc] = NULL; script.args = argv; script.num_args = argc; script.flags = SC_EXECABLE; script.uid = 0; script.gid = 0; if (truncate(global_data->reload_check_config, 0) && errno != ENOENT) { /* The file exists, but truncate failed. It might be a character * device like /dev/null. truncate() returns EINVAL in this case. */ if (stat(global_data->reload_check_config, &sb) || !S_ISCHR(sb.st_mode)) log_message(LOG_INFO, "truncate of config check log %s failed (%d) - %m", global_data->reload_check_config, errno); } create_reload_file(); /* Execute the script in a child process. Parent returns, child doesn't */ ret = system_call_script(master, reload_check_child_thread, NULL, 5 * TIMER_HZ, &script); if (ret) log_message(LOG_INFO, "Could not run config-test"); /* Restore CLOEXEC on config_copy fd */ /* coverity[check_return] - what are we going to do if this fails? */ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); FREE(argv); FREE(config_test_str); FREE_PTR(config_fd_str); } void start_reload(thread_ref_t thread) { if (thread && __test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Processing queued reload"); /* if reload_check_config is configured, validate the new config before reload */ if (!global_data->reload_check_config) { do_reload(); return; } if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "validate conf before Reload"); start_validate_reload_conf_child(); } static void process_reload_signal(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { if (!num_reloading) start_reload(NULL); else queue_reload(); } #endif #ifdef THREAD_DUMP void thread_dump_signal(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { #ifndef _ONE_PROCESS_DEBUG_ if (prog_type == PROG_TYPE_PARENT) propagate_signal(NULL, sig); #endif dump_thread_data(master, NULL); } #endif #ifndef _ONE_PROCESS_DEBUG_ /* Terminate handler */ static void sigend(__attribute__((unused)) void *v, __attribute__((unused)) int sig) { int ret; int wait_count = 0; struct timeval start_time, now; size_t i; int wstatus; int timeout = child_wait_time * 1000; int signal_fd = master->signal_fd; struct signalfd_siginfo siginfo; sigset_t sigmask; struct epoll_event ev = { .events = EPOLLIN, .data.fd = master->signal_fd }; int efd; log_message(LOG_INFO, "Stopping"); #ifdef _USE_SYSTEMD_NOTIFY_ systemd_notify_stopping(); #endif #ifndef _ONE_PROCESS_DEBUG_ if (global_data->reload_time_file) stop_reload_monitor(); #endif /* We only want to receive SIGCHLD now */ sigemptyset(&sigmask); sigaddset(&sigmask, SIGCHLD); signalfd(signal_fd, &sigmask, 0); /* Signal our children to terminate */ for (i = 0; i < NUM_CHILD_TERM; i++) { if (*children_term[i].pid_p > 0) { if (kill(*children_term[i].pid_p, SIGTERM)) { /* ESRCH means no such process */ if (errno == ESRCH) *children_term[i].pid_p = 0; } else wait_count++; } } efd = epoll_create(1); epoll_ctl(efd, EPOLL_CTL_ADD, signal_fd, &ev); gettimeofday(&start_time, NULL); while (wait_count) { ret = epoll_wait(efd, &ev, 1, timeout); if (ret == 0) break; if (ret == -1) { if (check_EINTR(errno)) continue; log_message(LOG_INFO, "Terminating epoll_wait returned errno %d", errno); break; } if (ev.data.fd != signal_fd) { log_message(LOG_INFO, "Terminating epoll_wait did not return signal_fd"); continue; } if (read(signal_fd, &siginfo, sizeof(siginfo)) != sizeof(siginfo)) { log_message(LOG_INFO, "Terminating signal read did not read entire siginfo"); break; } /* We are only expecting SIGCHLD */ if (siginfo.ssi_signo != SIGCHLD) { log_message(LOG_INFO, "Received signal %u code %d status %d from pid %u" " while waiting for children to terminate" , siginfo.ssi_signo, siginfo.ssi_code , siginfo.ssi_status, siginfo.ssi_pid); continue; } if (siginfo.ssi_code != CLD_EXITED && siginfo.ssi_code != CLD_KILLED && siginfo.ssi_code != CLD_DUMPED) { /* CLD_STOPPED, CLD_CONTINUED or CLD_TRAPPED */ log_message(LOG_INFO, "Received SIGCHLD code %d status %d from pid %u" " while waiting for children to terminate" , siginfo.ssi_code, siginfo.ssi_status, siginfo.ssi_pid); continue; } for (i = 0; i < NUM_CHILD_TERM && wait_count; i++) { if (*children_term[i].pid_p > 0 && *children_term[i].pid_p == (pid_t)siginfo.ssi_pid) { ret = waitpid(*children_term[i].pid_p, &wstatus, WNOHANG); if (ret == 0) continue; if (ret == -1) { if (!check_EINTR(errno)) log_message(LOG_INFO, "Wait for %s child return errno %d" , children_term[i].short_name, errno); continue; } report_child_status(wstatus, *children_term[i].pid_p, children_term[i].name); /* We could check ret == *children_term[i].pid_p, but it seems unneccessary */ *children_term[i].pid_p = 0; wait_count--; break; } } if (wait_count) { gettimeofday(&now, NULL); timeout = (child_wait_time - (now.tv_sec - start_time.tv_sec)) * 1000 + (start_time.tv_usec - now.tv_usec) / 1000; if (timeout < 0) break; } } close(efd); /* A child may not have terminated, so force its termination */ for (i = 0; i < NUM_CHILD_TERM; i++) { if (*children_term[i].pid_p) { log_message(LOG_INFO, "%s process failed to die - forcing termination" , children_term[i].short_name); kill(*children_term[i].pid_p, SIGKILL); } } if (!global_data->shutdown_script) { /* register the terminate thread */ thread_add_terminate_event(master); } else { /* If we have a shutdown script, run it now */ if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Running shutdown script %s" , global_data->shutdown_script->args[0]); if (system_call_script(master, shutdown_script_completed, NULL , global_data->shutdown_script_timeout * TIMER_HZ , global_data->shutdown_script) == -1) log_message(LOG_INFO, "Call of shutdown script %s failed" , global_data->shutdown_script->args[0]); } } #endif /* Initialize signal handler */ static void signal_init(void) { #ifndef _ONE_PROCESS_DEBUG_ signal_set(SIGHUP, process_reload_signal, NULL); signal_set(SIGUSR1, propagate_signal, NULL); signal_set(SIGUSR2, propagate_signal, NULL); signal_set(SIGSTATS_CLEAR, propagate_signal, NULL); #ifdef _WITH_JSON_ signal_set(SIGJSON, propagate_signal, NULL); #endif if (ignore_sigint) signal_ignore(SIGINT); else signal_set(SIGINT, sigend, NULL); signal_set(SIGTERM, sigend, NULL); #ifdef THREAD_DUMP signal_set(SIGTDUMP, thread_dump_signal, NULL); #endif #endif signal_ignore(SIGPIPE); } static void signals_ignore(void) { #ifndef _ONE_PROCESS_DEBUG_ signal_ignore(SIGHUP); signal_ignore(SIGUSR1); signal_ignore(SIGUSR2); signal_ignore(SIGSTATS_CLEAR); #ifdef _WITH_JSON_ signal_ignore(SIGJSON); #endif #endif } /* To create a core file when abrt is running (a RedHat distribution), * and keepalived isn't installed from an RPM package, edit the file * “/etc/abrt/abrt.confâ€, and change the value of the field * “ProcessUnpackaged†to “yesâ€. * * Alternatively, use the -M command line option. */ static void update_core_dump_pattern(const char *pattern_str) { int fd; bool initialising = (orig_core_dump_pattern == NULL); /* CORENAME_MAX_SIZE in kernel source include/linux/binfmts.h defines * the maximum string length, * see core_pattern[CORENAME_MAX_SIZE] in * fs/coredump.c. Currently (Linux 4.10) defines it to be 128, but the * definition is not exposed to user-space. */ #define CORENAME_MAX_SIZE 128 if (initialising) orig_core_dump_pattern = MALLOC(CORENAME_MAX_SIZE); fd = open ("/proc/sys/kernel/core_pattern", O_RDWR); if (fd == -1 || (initialising && read(fd, orig_core_dump_pattern, CORENAME_MAX_SIZE - 1) == -1) || write(fd, pattern_str, strlen(pattern_str)) == -1) { log_message(LOG_INFO, "Unable to read/write core_pattern"); if (fd != -1) close(fd); FREE(orig_core_dump_pattern); return; } close(fd); if (!initialising) FREE_PTR(orig_core_dump_pattern); } static void core_dump_init(void) { struct rlimit orig_rlim, rlim; if (set_core_dump_pattern) { /* If we set the core_pattern here, we will attempt to restore it when we * exit. This will be fine if it is a child of ours that core dumps, * but if we ourself core dump, then the core_pattern will not be restored */ update_core_dump_pattern(core_dump_pattern); } if (create_core_dump) { rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; if (getrlimit(RLIMIT_CORE, &orig_rlim) == -1) log_message(LOG_INFO, "Failed to get core file size"); else if (setrlimit(RLIMIT_CORE, &rlim) == -1) log_message(LOG_INFO, "Failed to set core file size"); else set_child_rlimit(RLIMIT_CORE, &orig_rlim); } } static mode_t set_umask(const char *opt_arg) { long umask_long; mode_t umask_bits; char *endptr; umask_long = strtoll(opt_arg, &endptr, 0); if (*endptr || umask_long < 0 || umask_long & ~(S_IRWXU | S_IRWXG | S_IRWXO)) { fprintf(stderr, "Invalid --umask option %s", opt_arg); return 0; } umask_bits = umask_long & (S_IRWXU | S_IRWXG | S_IRWXO); umask(umask_bits); umask_cmdline = true; #ifdef _MEM_CHECK_ update_mem_check_log_perms(umask_bits); #endif #ifdef ENABLE_LOG_TO_FILE update_log_file_perms(umask_bits); #endif return umask_bits; } RELAX_SUGGEST_ATTRIBUTE_CONST_START void initialise_debug_options(void) { #if defined WITH_DEBUG_OPTIONS && !defined _ONE_PROCESS_DEBUG_ char mask = 0; if (prog_type == PROG_TYPE_PARENT) mask = 1 << PROG_TYPE_PARENT; #ifdef _WITH_BFD_ else if (prog_type == PROG_TYPE_BFD) mask = 1 << PROG_TYPE_BFD; #endif #ifdef _WITH_LVS_ else if (prog_type == PROG_TYPE_CHECKER) mask = 1 << PROG_TYPE_CHECKER; #endif #ifdef _WITH_VRRP_ else if (prog_type == PROG_TYPE_VRRP) mask = 1 << PROG_TYPE_VRRP; #endif #ifdef _TIMER_CHECK_ do_timer_check = !!(timer_debug & mask); #endif #ifdef _SMTP_ALERT_DEBUG_ do_smtp_alert_debug = !!(smtp_debug & mask); #endif #ifdef _SMTP_CONNECT_DEBUG_ do_smtp_connect_debug = !!(smtp_connect_debug & mask); #endif #ifdef _EPOLL_DEBUG_ do_epoll_debug = !!(epoll_debug & mask); #endif #ifdef _EPOLL_THREAD_DUMP_ do_epoll_thread_dump = !!(epoll_thread_debug & mask); #endif #ifdef _REGEX_DEBUG_ do_regex_debug = !!(regex_debug & mask); #endif #ifdef _WITH_REGEX_TIMERS_ do_regex_timers = !!(regex_timers & mask); #endif #ifdef _TSM_DEBUG_ do_tsm_debug = !!(tsm_debug & mask); #endif #ifdef _VRRP_FD_DEBUG_ do_vrrp_fd_debug = !!(vrrp_fd_debug & mask); #endif #ifdef _NETLINK_TIMERS_ do_netlink_timers = !!(netlink_timer_debug & mask); #endif #ifdef _NETWORK_TIMESTAMP_ do_network_timestamp = !!(network_timestamp_debug & mask); #endif #ifdef _CHECKSUM_DEBUG_ do_checksum_debug = !!(checksum_debug & mask); #endif #ifdef _WITH_TRACK_PROCESS_ #ifdef _TRACK_PROCESS_DEBUG_ do_track_process_debug_detail = !!(track_process_debug_detail & mask); do_track_process_debug = !!(track_process_debug & mask) | do_track_process_debug_detail; #endif #endif #ifdef _PARSER_DEBUG_ do_parser_debug = !!(parser_debug & mask); #endif #ifdef _CHECKER_DEBUG_ do_checker_debug = !!(checker_debug & mask); #endif #ifdef _MEM_ERR_DEBUG_ set_keepalived_mem_err_debug(!!(mem_err_debug & mask)); #endif #ifdef _RECVMSG_DEBUG_ do_recvmsg_debug = !!(recvmsg_debug & mask); do_recvmsg_debug_dump = !!(recvmsg_debug_dump & mask); #endif #ifdef _EINTR_DEBUG_ do_eintr_debug = !!(eintr_debug & mask); #endif #ifdef _SCRIPT_DEBUG_ do_script_debug = !!(script_debug & mask); #endif #ifdef _DUMP_KEYWORDS_ do_dump_keywords = !!(dump_keywords & mask); #endif #endif } RELAX_SUGGEST_ATTRIBUTE_CONST_END #ifdef WITH_DEBUG_OPTIONS static void set_debug_options(const char *options) { char all_processes, processes; char opt; const char *opt_p = options; #ifdef _ONE_PROCESS_DEBUG_ all_processes = 1; #else all_processes = (1 << PROG_TYPE_PARENT); #ifdef _WITH_BFD_ all_processes |= (1 << PROG_TYPE_BFD); #endif #ifdef _WITH_LVS_ all_processes |= (1 << PROG_TYPE_CHECKER); #endif #ifdef _WITH_VRRP_ all_processes |= (1 << PROG_TYPE_VRRP); #endif #endif if (!options) { #ifdef _TIMER_CHECK_ timer_debug = all_processes; #endif #ifdef _SMTP_ALERT_DEBUG_ smtp_debug = all_processes; #endif #ifdef _SMTP_CONNECT_DEBUG_ smtp_connect_debug = all_processes; #endif #ifdef _EPOLL_DEBUG_ epoll_debug = all_processes; #endif #ifdef _EPOLL_THREAD_DUMP_ epoll_thread_debug = all_processes; #endif #ifdef _REGEX_DEBUG_ regex_debug = all_processes; #endif #ifdef _WITH_REGEX_TIMERS_ regex_timers = all_processes; #endif #ifdef _TSM_DEBUG_ tsm_debug = all_processes; #endif #ifdef _VRRP_FD_DEBUG_ vrrp_fd_debug = all_processes; #endif #ifdef _NETLINK_TIMERS_ netlink_timer_debug = all_processes; #endif #ifdef _NETWORK_TIMESTAMP_ network_timestamp_debug = all_processes; #endif #ifdef _CHECKSUM_DEBUG_ checksum_debug = all_processes; #endif #ifdef _TRACK_PROCESS_DEBUG_ track_process_debug = all_processes; track_process_debug_detail = all_processes; #endif #ifdef _PARSER_DEBUG_ parser_debug = all_processes; #endif #ifdef _CHECKER_DEBUG_ checker_debug = all_processes; #endif #ifdef _MEM_ERR_DEBUG_ mem_err_debug = all_processes; #endif #ifdef _RECVMSG_DEBUG_ recvmsg_debug = all_processes; recvmsg_debug_dump = all_processes; #endif #ifdef _EINTR_DEBUG_ eintr_debug = all_processes; #endif #ifdef _SCRIPT_DEBUG_ script_debug = all_processes; #endif #ifdef _DUMP_KEYWORDS_ dump_keywords = all_processes; #endif return; } opt_p = options; while (*opt_p) { if (!isupper(*opt_p)) { fprintf(stderr, "Unknown debug option'%c' in '%s'\n", *opt_p, options); return; } opt = *opt_p++; #ifdef _ONE_PROCESS_DEBUG_ processes = all_processes; #else if (!*opt_p || isupper(*opt_p)) processes = all_processes; else { processes = 0; while (*opt_p && !isupper(*opt_p)) { switch (*opt_p) { case 'p': processes |= (1 << PROG_TYPE_PARENT); break; #ifdef _WITH_BFD_ case 'b': processes |= (1 << PROG_TYPE_BFD); break; #endif #ifdef _WITH_LVS_ case 'c': processes |= (1 << PROG_TYPE_CHECKER); break; #endif #ifdef _WITH_VRRP_ case 'v': processes |= (1 << PROG_TYPE_VRRP); break; #endif default: fprintf(stderr, "Unknown debug process '%c' in '%s'\n", *opt_p, options); return; } opt_p++; } } #endif /* Letters used - ABCDEFGHIJKMNOPRSTUVXZ */ switch (opt) { #ifdef _TIMER_CHECK_ case 'T': timer_debug = processes; break; #endif #ifdef _SMTP_ALERT_DEBUG_ case 'M': smtp_debug = processes; break; #endif #ifdef _SMTP_CONNECT_DEBUG_ case 'B': smtp_connect_debug = processes; break; #endif #ifdef _EPOLL_DEBUG_ case 'E': epoll_debug = processes; break; #endif #ifdef _EPOLL_THREAD_DUMP_ case 'D': epoll_thread_debug = processes; break; #endif #ifdef _REGEX_DEBUG_ case 'R': regex_debug = processes; break; #endif #ifdef _WITH_REGEX_TIMERS_ case 'X': regex_timers = processes; break; #endif #ifdef _TSM_DEBUG_ case 'S': tsm_debug = processes; break; #endif #ifdef _VRRP_FD_DEBUG_ case 'F': vrrp_fd_debug = processes; break; #endif #ifdef _NETLINK_TIMERS_ case 'N': netlink_timer_debug = processes; break; #endif #ifdef _NETWORK_TIMESTAMP_ case 'P': network_timestamp_debug = processes; break; #endif #ifdef _CHECKSUM_DEBUG_ case 'U': checksum_debug = processes; break; #endif #ifdef _TRACK_PROCESS_DEBUG_ case 'O': track_process_debug = processes; break; case 'A': track_process_debug_detail = processes; break; #endif #ifdef _PARSER_DEBUG_ case 'C': parser_debug = processes; break; #endif #ifdef _CHECKER_DEBUG_ case 'H': checker_debug = processes; break; #endif #ifdef _MEM_ERR_DEBUG_ case 'Z': mem_err_debug = processes; break; #endif #ifdef _RECVMSG_DEBUG_ case 'G': recvmsg_debug = processes; break; case 'J': recvmsg_debug_dump = processes; break; #endif #ifdef _EINTR_DEBUG_ case 'I': eintr_debug = processes; break; #endif #ifdef _SCRIPT_DEBUG_ case 'V': script_debug = processes; break; #endif #ifdef _DUMP_KEYWORDS_ case 'K': dump_keywords = processes; break; #endif default: fprintf(stderr, "Unknown debug type '%c' in '%s'\n", opt, options); return; } } } #endif static void report_distro(void) { FILE *fp = fopen("/etc/os-release", "r"); char buf[128]; const char * const var = "PRETTY_NAME="; const size_t var_len = strlen(var); char *distro_name; size_t distro_len; if (!fp) return; while (fgets(buf, sizeof(buf), fp)) { if (!strncmp(buf, var, var_len)) { distro_name = buf + var_len; /* Remove "'s and trailing \n */ if (*distro_name == '"') distro_name++; distro_len = strlen(distro_name); if (distro_len && distro_name[distro_len - 1] == '\n') distro_name[--distro_len] = '\0'; if (distro_len && distro_name[distro_len - 1] == '"') distro_name[--distro_len] = '\0'; fprintf(stderr, "Distro: %s\n", distro_name); break; } } fclose(fp); } #ifdef _REPRODUCIBLE_BUILD_ static char * read_config_opts(const char *filename) { struct stat statbuf; int fd; char *opts_buf; if (stat(filename, &statbuf)) return NULL; if ((fd = open(filename, O_RDONLY)) == -1) { fprintf(stderr, "Failed to open %s\n", filename); return NULL; } opts_buf = malloc(statbuf.st_size); /* Read, skipping trailing \n */ if (read(fd, opts_buf, statbuf.st_size - 1) != statbuf.st_size - 1) { fprintf(stderr, "Failed to read %s\n", filename); close(fd); free(opts_buf); return NULL; } opts_buf[statbuf.st_size - 1] = '\0'; close(fd); return opts_buf; } #endif /* Usage function */ static void usage(const char *prog) { fprintf(stderr, "Usage: %s [OPTION...]\n", prog); fprintf(stderr, " -f, --use-file=FILE Use the specified configuration file\n" " default '%s'\n", DEFAULT_CONFIG_FILE); #ifdef OLD_DEFAULT_CONFIG_FILE fprintf(stderr, " or '%s'\n", OLD_DEFAULT_CONFIG_FILE); #endif #if defined _WITH_VRRP_ && defined _WITH_LVS_ fprintf(stderr, " -P, --vrrp Only run with VRRP subsystem\n"); fprintf(stderr, " -C, --check Only run with Health-checker subsystem\n"); #endif #ifdef _WITH_BFD_ fprintf(stderr, " -B, --no_bfd Don't run BFD subsystem\n"); #endif fprintf(stderr, " --all Force all child processes to run, even if have no configuration\n"); fprintf(stderr, " -l, --log-console Log messages to local console\n"); fprintf(stderr, " -D, --log-detail Detailed log messages\n"); fprintf(stderr, " -S, --log-facility=([0-7]|local[0-7]|user|daemon)\n"); fprintf(stderr, " Set syslog facility to LOG_LOCAL[0-7], user or daemon (default)\n"); #ifdef ENABLE_LOG_TO_FILE fprintf(stderr, " -g, --log-file=FILE Also log to FILE (default %s/keepalived.log)\n", tmp_dir); fprintf(stderr, " --flush-log-file Flush log file on write\n"); #endif fprintf(stderr, " -G, --no-syslog Don't log via syslog\n"); fprintf(stderr, " -u, --umask=MASK umask for file creation (in numeric form)\n"); #ifdef _WITH_VRRP_ fprintf(stderr, " -X, --release-vips Drop VIP on transition from signal.\n"); fprintf(stderr, " -V, --dont-release-vrrp Don't remove VRRP VIPs and VROUTEs on daemon stop\n"); #endif #ifdef _WITH_LVS_ fprintf(stderr, " -I, --dont-release-ipvs Don't remove IPVS topology on daemon stop\n"); #endif fprintf(stderr, " -R, --dont-respawn Don't respawn child processes\n"); fprintf(stderr, " -n, --dont-fork Don't fork the daemon process\n"); fprintf(stderr, " -d, --dump-conf Dump the configuration data\n"); fprintf(stderr, " -p, --pid=FILE Use specified pidfile for parent process\n"); #ifdef _WITH_VRRP_ fprintf(stderr, " -r, --vrrp_pid=FILE Use specified pidfile for VRRP child process\n"); #endif #ifdef _WITH_LVS_ fprintf(stderr, " -T, --genhash Enter into genhash utility mode (this should be the first option used).\n"); fprintf(stderr, " -c, --checkers_pid=FILE Use specified pidfile for checkers child process\n"); fprintf(stderr, " -a, --address-monitoring Report all address additions/deletions notified via netlink\n"); #endif #ifdef _WITH_BFD_ fprintf(stderr, " -b, --bfd_pid=FILE Use specified pidfile for BFD child process\n"); #endif #ifdef _WITH_SNMP_ fprintf(stderr, " -x, --snmp Enable SNMP subsystem\n"); fprintf(stderr, " -A, --snmp-agent-socket=FILE Use the specified socket for master agent\n"); #endif fprintf(stderr, " -s, --namespace=NAME Run in network namespace NAME (overrides config)\n"); fprintf(stderr, " -m, --core-dump Produce core dump if terminate abnormally\n"); fprintf(stderr, " -M, --core-dump-pattern=PATN Also set /proc/sys/kernel/core_pattern to PATN (default 'core')\n"); #ifdef _MEM_CHECK_ fprintf(stderr, " --no-mem-check disable malloc() etc mem-checks\n"); #endif #ifdef _MEM_CHECK_LOG_ fprintf(stderr, " -L, --mem-check-log Log malloc/frees to syslog\n"); #endif #ifdef _OPENSSL_MEM_CHECK_ fprintf(stderr, " --openssl-mem-check Enable OpenSSL malloc() etc mem-checks\n"); #endif fprintf(stderr, " -e, --all-config Error if any configuration file missing (same as includet)\n"); fprintf(stderr, " -i, --config-id id Skip any configuration lines beginning '@' that don't match id\n" " or any lines beginning @^ that do match.\n" " The config-id defaults to the node name if option not used\n"); fprintf(stderr, " --ignore-sigint ignore SIGINT (default means terminate) - used for debugging with GDB\n"); fprintf(stderr, " --signum=SIGFUNC Return signal number for STOP, RELOAD, DATA, STATS, STATS_CLEAR" #ifdef _WITH_JSON_ ", JSON" #endif #ifdef THREAD_DUMP ", TDUMP" #endif "\n"); fprintf(stderr, " -t, --config-test[=LOG_FILE] Check the configuration for obvious errors, output to\n" " stderr by default\n"); /* fprintf(stderr, " --config-fd=fd_num File descriptor to write consolidated config to\n"); */ // Internal use only #ifdef _WITH_PERF_ fprintf(stderr, " --perf[=PERF_TYPE] Collect perf data, PERF_TYPE=all, run(default) or end\n"); #endif #ifdef WITH_DEBUG_OPTIONS fprintf(stderr, " --debug[=...] Enable debug options. p, b, c, v specify parent, bfd, checker and vrrp processes\n"); fprintf(stderr, " If no process(es) specified, the option will apply to all processes\n"); #ifdef _TIMER_CHECK_ fprintf(stderr, " T - timer debug\n"); #endif #ifdef _SMTP_ALERT_DEBUG_ fprintf(stderr, " M - email alert debug\n"); #endif #ifdef _SMTP_CONNECT_DEBUG_ fprintf(stderr, " B - smtp connect debug\n"); #endif #ifdef _EPOLL_DEBUG_ fprintf(stderr, " E - epoll debug\n"); #endif #ifdef _EPOLL_THREAD_DUMP_ fprintf(stderr, " D - epoll thread dump debug\n"); #endif #ifdef _VRRP_FD_DEBUG_ fprintf(stderr, " F - vrrp fd dump debug\n"); #endif #ifdef _REGEX_DEBUG_ fprintf(stderr, " R - regex debug\n"); #endif #ifdef _WITH_REGEX_TIMERS_ fprintf(stderr, " X - regex timers\n"); #endif #ifdef _TSM_DEBUG_ fprintf(stderr, " S - TSM debug\n"); #endif #ifdef _NETLINK_TIMERS_ fprintf(stderr, " N - netlink timer debug\n"); #endif #ifdef _NETWORK_TIMESTAMP_ fprintf(stderr, " P - network timestamp debug\n"); #endif #ifdef _CHECKSUM_DEBUG_ fprintf(stderr, " U - checksum diagnostics\n"); #endif #ifdef _TRACK_PROCESS_DEBUG_ fprintf(stderr, " O - track process debug\n"); fprintf(stderr, " A - track process debug with extra detail\n"); #endif #ifdef _PARSER_DEBUG_ fprintf(stderr, " C - parser (config) debug\n"); #endif #ifdef _CHECKER_DEBUG_ fprintf(stderr, " H - checker debug\n"); #endif #ifdef _MEM_ERR_DEBUG_ fprintf(stderr, " Z - memory alloc/free error debug\n"); #endif #ifdef _RECVMSG_DEBUG_ fprintf(stderr, " G - VRRP recvmsg() debug\n"); fprintf(stderr, " J - VRRP recvmsg() log rx data\n"); #endif #ifdef _EINTR_DEBUG_ fprintf(stderr, " I - EINTR debugging\n"); #endif #ifdef _SCRIPT_DEBUG_ fprintf(stderr, " V - script debugging\n"); #endif #ifdef _DUMP_KEYWORDS_ fprintf(stderr, " K - dump keywords\n"); #endif fprintf(stderr, " Example --debug=TpMEvcp\n"); #endif fprintf(stderr, " -v, --version Display the version number\n"); fprintf(stderr, " -h, --help Display this help message\n"); } /* Command line parser */ static bool parse_cmdline(int argc, char **argv) { int c; bool reopen_log = false; int signum; struct utsname uname_buf; int longindex; int curind; bool bad_option = false; unsigned facility; mode_t new_umask_val; unsigned i; #ifdef _WITH_LVS_ bool first_option; #endif struct option long_options[] = { {"use-file", required_argument, NULL, 'f'}, #if defined _WITH_VRRP_ && defined _WITH_LVS_ {"vrrp", no_argument, NULL, 'P'}, {"check", no_argument, NULL, 'C'}, #endif #ifdef _WITH_BFD_ {"no_bfd", no_argument, NULL, 'B'}, #endif {"all", no_argument, NULL, 3 }, {"log-console", no_argument, NULL, 'l'}, {"log-detail", no_argument, NULL, 'D'}, {"log-facility", required_argument, NULL, 'S'}, {"log-file", optional_argument, NULL, 'g'}, {"all-config", no_argument, NULL, 'e'}, #ifdef ENABLE_LOG_TO_FILE {"flush-log-file", no_argument, NULL, 2 }, #endif {"no-syslog", no_argument, NULL, 'G'}, {"umask", required_argument, NULL, 'u'}, #ifdef _WITH_VRRP_ {"release-vips", no_argument, NULL, 'X'}, {"dont-release-vrrp", no_argument, NULL, 'V'}, #endif #ifdef _WITH_LVS_ {"dont-release-ipvs", no_argument, NULL, 'I'}, #endif {"dont-respawn", no_argument, NULL, 'R'}, {"dont-fork", no_argument, NULL, 'n'}, {"dump-conf", no_argument, NULL, 'd'}, {"pid", required_argument, NULL, 'p'}, #ifdef _WITH_VRRP_ {"vrrp_pid", required_argument, NULL, 'r'}, #endif #ifdef _WITH_LVS_ {"genhash", no_argument, NULL, 'T'}, {"checkers_pid", required_argument, NULL, 'c'}, {"address-monitoring", no_argument, NULL, 'a'}, #endif #ifdef _WITH_BFD_ {"bfd_pid", required_argument, NULL, 'b'}, #endif #ifdef _WITH_SNMP_ {"snmp", no_argument, NULL, 'x'}, {"snmp-agent-socket", required_argument, NULL, 'A'}, #endif {"core-dump", no_argument, NULL, 'm'}, {"core-dump-pattern", optional_argument, NULL, 'M'}, #ifdef _MEM_CHECK_ {"no-mem-check", no_argument, NULL, 7 }, #endif #ifdef _MEM_CHECK_LOG_ {"mem-check-log", no_argument, NULL, 'L'}, #endif #ifdef _OPENSSL_MEM_CHECK_ {"openssl-mem-check", no_argument, NULL, 'O' }, #endif {"namespace", required_argument, NULL, 's'}, {"config-id", required_argument, NULL, 'i'}, {"signum", required_argument, NULL, 4 }, {"config-test", optional_argument, NULL, 't'}, {"config-fd", required_argument, NULL, 8 }, {"ignore-sigint", no_argument, NULL, 9 }, #ifdef _WITH_PERF_ {"perf", optional_argument, NULL, 5 }, #endif #ifdef WITH_DEBUG_OPTIONS {"debug", optional_argument, NULL, 6 }, #endif {"version", no_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0 } }; /* Unfortunately, if a short option is used, getopt_long() doesn't change the value * of longindex, so we need to ensure that before calling getopt_long(), longindex * is set to a known invalid value */ curind = optind; #ifdef _WITH_LVS_ first_option = true; #endif /* Used short options: ABCDGILMPRSVXabcdefghilmnprstuvx */ while (longindex = -1, (c = getopt_long(argc, argv, ":vhlndu:DRS:f:p:i:es:mM::g::Gt::" #if defined _WITH_VRRP_ && defined _WITH_LVS_ "PC" #endif #ifdef _WITH_VRRP_ "r:VX" #endif #ifdef _WITH_LVS_ "ac:IT" #endif #ifdef _WITH_BFD_ "Bb:" #endif #ifdef _WITH_SNMP_ "xA:" #endif #ifdef _MEM_CHECK_LOG_ "L" #endif #ifdef _OPENSSL_MEM_CHECK_ "O" #endif , long_options, &longindex)) != -1) { /* Check for an empty option argument. For example --use-file= returns * a 0 length option, which we don't want */ if (longindex >= 0 && long_options[longindex].has_arg == required_argument && optarg && !optarg[0]) c = ':'; switch (c) { case 'v': fprintf(stderr, "%s", version_string); #ifdef GIT_COMMIT fprintf(stderr, ", git commit %s", GIT_COMMIT); #endif fprintf(stderr, "\n\n%s\n\n", COPYRIGHT_STRING); fprintf(stderr, "Built with kernel headers for Linux %d.%d.%d\n", (LINUX_VERSION_CODE >> 16) & 0xff, (LINUX_VERSION_CODE >> 8) & 0xff, (LINUX_VERSION_CODE ) & 0xff); uname(&uname_buf); fprintf(stderr, "Running on %s %s %s\n", uname_buf.sysname, uname_buf.release, uname_buf.version); report_distro(); fprintf(stderr, "\n"); fprintf(stderr, "configure options: %s\n\n", config_opts); fprintf(stderr, "Config options: %s\n\n", CONFIGURATION_OPTIONS); fprintf(stderr, "System options: %s\n", SYSTEM_OPTIONS); exit(0); break; case 'h': usage(argv[0]); exit(0); break; case 'l': __set_bit(LOG_CONSOLE_BIT, &debug); reopen_log = true; break; case 'n': __set_bit(DONT_FORK_BIT, &debug); break; case 'd': __set_bit(DUMP_CONF_BIT, &debug); break; #ifdef _WITH_VRRP_ case 'V': __set_bit(DONT_RELEASE_VRRP_BIT, &debug); break; #endif #ifdef _WITH_LVS_ case 'I': __set_bit(DONT_RELEASE_IPVS_BIT, &debug); break; case 'T': if (!first_option) fprintf(stderr, "Warning -- `%s` not used as first option, previous options ignored\n", longindex == -1 ? "-T" : long_options[longindex].name); /* Set our process name */ prctl(PR_SET_NAME, "genhash"); check_genhash(false, argc, argv); exit(0); #endif case 'D': if (__test_bit(LOG_DETAIL_BIT, &debug)) __set_bit(LOG_EXTRA_DETAIL_BIT, &debug); else __set_bit(LOG_DETAIL_BIT, &debug); break; case 'R': __set_bit(DONT_RESPAWN_BIT, &debug); break; #ifdef _WITH_VRRP_ case 'X': __set_bit(RELEASE_VIPS_BIT, &debug); break; #endif case 'S': if (read_unsigned(optarg, &facility, 0, LOG_FACILITY_MAX, false) || (!strncmp(optarg, "local", 5) && read_unsigned(&optarg[5], &facility, 0, LOG_FACILITY_MAX, false))) { log_facility = LOG_FACILITY[facility].facility; reopen_log = true; } else { for (i = 0; i < sizeof(facility_names) / sizeof(facility_names[0]); i++) { if (!strcmp(optarg, facility_names[i].name)) { log_facility = facility_names[i].facility; reopen_log = true; break; } } if (!reopen_log) fprintf(stderr, "Invalid log facility '%s'\n", optarg); } break; case 'g': #ifdef ENABLE_LOG_TO_FILE if (optarg && optarg[0]) log_file_name = optarg; else log_file_name = "keepalived.log"; open_log_file(log_file_name, NULL, NULL, NULL); #else fprintf(stderr, "-g requires configure option --enable-log-file\n"); bad_option = true; #endif break; #ifdef ENABLE_LOG_TO_FILE case 2: /* --flush-log-file */ set_flush_log_file(); break; #endif case 'G': __set_bit(NO_SYSLOG_BIT, &debug); reopen_log = true; break; case 'u': /* coverity[var_deref_model] */ new_umask_val = set_umask(optarg); if (umask_cmdline) umask_val = new_umask_val; break; case 't': __set_bit(CONFIG_TEST_BIT, &debug); __set_bit(DONT_RESPAWN_BIT, &debug); __set_bit(DONT_FORK_BIT, &debug); __set_bit(NO_SYSLOG_BIT, &debug); if (optarg && optarg[0]) { int fd = open(optarg, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd == -1) { fprintf(stderr, "Unable to open config-test log file %s %d - %m\n", optarg, errno); exit(EXIT_FAILURE); } dup2(fd, STDERR_FILENO); close(fd); } break; case 'f': conf_file = optarg; break; #if defined _WITH_VRRP_ && defined _WITH_LVS_ case 'P': __clear_bit(DAEMON_CHECKERS, &daemon_mode); break; case 'C': __clear_bit(DAEMON_VRRP, &daemon_mode); break; #endif #ifdef _WITH_BFD_ case 'B': __clear_bit(DAEMON_BFD, &daemon_mode); break; #endif case 'p': main_pidfile.path = optarg; break; #ifdef _WITH_LVS_ case 'c': checkers_pidfile.path = optarg; break; case 'a': __set_bit(LOG_ADDRESS_CHANGES, &debug); break; #endif #ifdef _WITH_VRRP_ case 'r': vrrp_pidfile.path = optarg; break; #endif #ifdef _WITH_BFD_ case 'b': bfd_pidfile.path = optarg; break; #endif #ifdef _WITH_SNMP_ case 'x': snmp_option = true; break; case 'A': snmp_socket = optarg; break; #endif case 'M': set_core_dump_pattern = true; if (optarg && optarg[0]) core_dump_pattern = optarg; /* FALLTHROUGH */ case 'm': create_core_dump = true; break; #ifdef _MEM_CHECK_LOG_ case 'L': __set_bit(MEM_CHECK_LOG_BIT, &debug); break; #endif #ifdef _OPENSSL_MEM_CHECK_ case 'O': __set_bit(OPENSSL_MEM_CHECK_BIT, &debug); break; #endif case 's': override_namespace = optarg; break; case 'e': include_check_set(NULL); break; case 'i': FREE_CONST_PTR(config_id); config_id = STRDUP(optarg); break; case 4: /* --signum */ /* coverity[var_deref_model] */ signum = get_signum(optarg); if (signum == -1) { fprintf(stderr, "Unknown sigfunc %s\n", optarg); exit(1); } /* If we want to print the signal description, strsignal(signum) can be used */ printf("%d\n", signum); exit(0); break; case 3: /* --all */ __set_bit(RUN_ALL_CHILDREN, &daemon_mode); #ifdef _WITH_VRRP_ __set_bit(DAEMON_VRRP, &daemon_mode); #endif #ifdef _WITH_LVS_ __set_bit(DAEMON_CHECKERS, &daemon_mode); #endif #ifdef _WITH_BFD_ __set_bit(DAEMON_BFD, &daemon_mode); #endif break; #ifdef _WITH_PERF_ case 5: if (optarg && optarg[0]) { if (!strcmp(optarg, "run")) perf_run = PERF_RUN; else if (!strcmp(optarg, "all")) perf_run = PERF_ALL; else if (!strcmp(optarg, "end")) perf_run = PERF_END; else log_message(LOG_INFO, "Unknown perf start point %s", optarg); } else perf_run = PERF_RUN; break; #endif #ifdef WITH_DEBUG_OPTIONS case 6: set_debug_options(optarg && optarg[0] ? optarg : NULL); break; #endif #ifdef _MEM_CHECK_ case 7: __clear_bit(MEM_CHECK_BIT, &debug); break; #endif case 8: set_config_fd(atoi(optarg)); break; case 9: ignore_sigint = true; break; case '?': if (optopt && argv[curind][1] != '-') fprintf(stderr, "Unknown option -%c\n", optopt); else fprintf(stderr, "Unknown option %s\n", argv[curind]); bad_option = true; break; case ':': if (optopt && argv[curind][1] != '-') fprintf(stderr, "Missing parameter for option -%c\n", optopt); else fprintf(stderr, "Missing parameter for option --%s\n", long_options[longindex].name); bad_option = true; break; default: exit(1); break; } curind = optind; #ifdef _WITH_LVS_ first_option = false; #endif } if (optind < argc) { printf("Unexpected argument(s): "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } if (bad_option) exit(1); #ifdef _MEM_CHECK_ if (__test_bit(CONFIG_TEST_BIT, &debug)) __clear_bit(MEM_CHECK_BIT, &debug); #endif return reopen_log; } #ifdef THREAD_DUMP static void register_parent_thread_addresses(void) { register_scheduler_addresses(); register_signal_thread_addresses(); #ifndef _ONE_PROCESS_DEBUG_ register_config_notify_addresses(); #endif #ifdef _WITH_LVS_ register_check_parent_addresses(); #endif #ifdef _WITH_VRRP_ register_vrrp_parent_addresses(); #endif #ifdef _WITH_BFD_ register_bfd_parent_addresses(); #endif #ifndef _ONE_PROCESS_DEBUG_ register_reload_addresses(); register_signal_handler_address("propagate_signal", propagate_signal); register_signal_handler_address("sigend", sigend); #endif register_signal_handler_address("thread_child_handler", thread_child_handler); #ifdef THREAD_DUMP register_signal_handler_address("thread_dump_signal", thread_dump_signal); #endif register_thread_address("start_keepalived", start_keepalived); register_thread_address("startup_script_completed", startup_script_completed); register_thread_address("shutdown_script_completed", shutdown_script_completed); register_thread_address("run_startup_script", run_startup_script); register_thread_address("print_parent_data", print_parent_data); } #endif /* Entry point */ int keepalived_main(int argc, char **argv) { bool report_stopped = true; struct utsname uname_buf; char *end; int exit_code = KEEPALIVED_EXIT_OK; magic_t magic; unsigned script_flags; struct rusage usage; struct rusage child_usage; #ifdef OLD_DEFAULT_CONFIG_FILE struct stat statbuf; #endif #ifdef _WITH_LVS_ char *name = strrchr(argv[0], '/'); if (!strcmp(name ? name + 1 : argv[0], "genhash")) { check_genhash(true, argc, argv); /* Not reached */ } #endif #ifdef _WITH_SANITIZER_ sanitizer_init(); #endif #ifdef _REPRODUCIBLE_BUILD_ char *config_opts_read; if (!(config_opts_read = read_config_opts(CONFIG_OPTS_FILE_PRIMARY))) { /* Look for the config-opts file in same location as executable */ const char *suffix = ".config-opts"; char *file = malloc(strlen(argv[0]) + strlen(suffix) + 1); strcpy(file, argv[0]); strcat(file, suffix); config_opts_read = read_config_opts(file); free(file); } if (!config_opts_read) { fprintf(stderr, "Unable to read build config options file\n"); exit(1); } config_opts = config_opts_read; #endif #ifdef _MEM_CHECK_ __set_bit(MEM_CHECK_BIT, &debug); #endif /* Ignore reloading signals till signal_init call */ signals_ignore(); /* Ensure time_now is set. We then don't have to check anywhere * else if it is set. */ set_time_now(); /* Is there a TMPDIR override? */ set_tmp_dir(); set_our_uid_gid(); /* Save command line options in case need to log them later */ save_cmd_line_options(argc, argv); #ifdef _USE_SYSTEMD_NOTIFY_ #ifndef _ONE_PROCESS_DEBUG_ check_parent_systemd(); #endif #endif /* We are the parent process */ #ifndef _ONE_PROCESS_DEBUG_ prog_type = PROG_TYPE_PARENT; #endif /* Initialise daemon_mode */ #ifdef _WITH_VRRP_ __set_bit(DAEMON_VRRP, &daemon_mode); #endif #ifdef _WITH_LVS_ __set_bit(DAEMON_CHECKERS, &daemon_mode); #endif #ifdef _WITH_BFD_ __set_bit(DAEMON_BFD, &daemon_mode); #endif /* Set default file creation mask */ umask(umask_val); /* Open log with default settings so we can log initially */ open_syslog(PACKAGE_NAME); #ifdef _MEM_CHECK_ mem_log_init(PACKAGE_NAME, "Parent process"); #endif /* Some functionality depends on kernel version, so get the version here */ if (uname(&uname_buf)) log_message(LOG_INFO, "Unable to get uname() information - error %d", errno); else { os_major = (unsigned)strtoul(uname_buf.release, &end, 10); if (*end != '.') os_major = 0; else { os_minor = (unsigned)strtoul(end + 1, &end, 10); if (*end != '.') os_major = 0; else { if (!isdigit(end[1])) os_major = 0; else os_release = (unsigned)strtoul(end + 1, &end, 10); } } if (!os_major) log_message(LOG_INFO, "Unable to parse kernel version %s", uname_buf.release); /* config_id defaults to hostname */ if (!config_id) { end = strchrnul(uname_buf.nodename, '.'); config_id = STRNDUP(uname_buf.nodename, (size_t)(end - uname_buf.nodename)); } } /* * Parse command line and set debug level. * bits 0..7 reserved by main.c */ if (parse_cmdline(argc, argv)) { closelog(); if (!__test_bit(NO_SYSLOG_BIT, &debug)) open_syslog(PACKAGE_NAME); } if (__test_bit(LOG_CONSOLE_BIT, &debug)) enable_console_log(); #ifdef GIT_COMMIT log_message(LOG_INFO, "Starting %s, git commit %s", version_string, GIT_COMMIT); #else log_message(LOG_INFO, "Starting %s", version_string); #endif /* Handle any core file requirements */ core_dump_init(); #ifdef _REPRODUCIBLE_BUILD_ /* We want to use our MALLOC functions */ char *new_config_opts_str = STRDUP(config_opts); free(config_opts_read); config_opts = new_config_opts_str; #endif if (os_major) { if (KERNEL_VERSION(os_major, os_minor, os_release) < LINUX_VERSION_CODE) { /* keepalived was built for a later kernel version */ log_message(LOG_INFO, "WARNING - keepalived was built for newer Linux %d.%d.%d, running on %s %s %s", (LINUX_VERSION_CODE >> 16) & 0xff, (LINUX_VERSION_CODE >> 8) & 0xff, (LINUX_VERSION_CODE ) & 0xff, uname_buf.sysname, uname_buf.release, uname_buf.version); } else { /* keepalived was built for a later kernel version */ log_message(LOG_INFO, "Running on %s %s %s (built for Linux %d.%d.%d)", uname_buf.sysname, uname_buf.release, uname_buf.version, (LINUX_VERSION_CODE >> 16) & 0xff, (LINUX_VERSION_CODE >> 8) & 0xff, (LINUX_VERSION_CODE ) & 0xff); } } log_command_line(0); /* If no configuration file has been specified, select the * first default config file that exists. */ if (!conf_file) { conf_file = DEFAULT_CONFIG_FILE; #ifdef OLD_DEFAULT_CONFIG_FILE /* If DEFAULT_CONFIG_FILE doesn't exist and * OLD_DEFAULT_CONFIG_FILE does exist, use the * latter */ if (stat(DEFAULT_CONFIG_FILE, &statbuf) && !stat(OLD_DEFAULT_CONFIG_FILE, &statbuf)) { conf_file = OLD_DEFAULT_CONFIG_FILE; log_message(LOG_INFO, "WARNING - using deprecated default config file '%s' - please move to '%s'", OLD_DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE); } #endif } /* Check we can read the configuration file(s). NOTE: the working directory will be / if we forked, but will be the current working directory when keepalived was run if we haven't forked. This means that if any config file names are not absolute file names, the behaviour will be different depending on whether we forked or not. */ if (!check_conf_file(conf_file)) { if (__test_bit(CONFIG_TEST_BIT, &debug)) config_test_exit(); exit_code = KEEPALIVED_EXIT_NO_CONFIG; goto end; } global_data = alloc_global_data(); // Change here so don't need check_conf_file() read_config_file(true); if (had_config_file_error()) { exit_code = KEEPALIVED_EXIT_NO_CONFIG; goto end; } init_global_data(global_data, NULL, false); #if defined _WITH_VRRP_ && defined _WITH_NFTABLES_ if (global_data->vrrp_nf_table_name) set_nf_ifname_type(); #endif /* Update process name if necessary */ if (global_data->process_name) set_process_name(global_data->process_name); if (override_namespace) { if (global_data->network_namespace) { log_message(LOG_INFO, "Overriding config net_namespace '%s' with command line namespace '%s'", global_data->network_namespace, override_namespace); FREE_CONST(global_data->network_namespace); } global_data->network_namespace = STRDUP(override_namespace); } if (!__test_bit(CONFIG_TEST_BIT, &debug) && (global_data->instance_name || global_data->network_namespace)) { if ((syslog_ident = make_syslog_ident(PACKAGE_NAME))) { log_message(LOG_INFO, "Changing syslog ident to %s", syslog_ident); closelog(); open_syslog(syslog_ident); } else log_message(LOG_INFO, "Unable to change syslog ident"); use_pid_dir = true; #ifdef ENABLE_LOG_TO_FILE open_log_file(log_file_name, NULL, global_data->network_namespace, global_data->instance_name); #endif } /* Initialise pointer to child finding function */ set_child_finder_name(find_keepalived_child_name); if (!__test_bit(CONFIG_TEST_BIT, &debug)) { if (use_pid_dir) { /* Create the directory for pid files */ create_pid_dir(); } } if (global_data->network_namespace) { /* If we want to monitor processes, we have to do it before calling * setns(), so start it here if we are using network namespaces. */ #ifdef _WITH_TRACK_PROCESS_ if (!__test_bit(CONFIG_TEST_BIT, &debug)) open_track_processes(); #endif if (!set_namespaces(global_data->network_namespace)) { log_message(LOG_ERR, "Unable to set network namespace %s - exiting", global_data->network_namespace); goto end; } } if (!__test_bit(CONFIG_TEST_BIT, &debug)) { if (global_data->instance_name) { if (!main_pidfile.path && (main_pidfile.path = make_pidfile_name(KEEPALIVED_PID_DIR KEEPALIVED_PID_FILE, global_data->instance_name, PID_EXTENSION))) main_pidfile.free_path = true; #ifdef _WITH_LVS_ if (!checkers_pidfile.path && (checkers_pidfile.path = make_pidfile_name(KEEPALIVED_PID_DIR CHECKERS_PID_FILE, global_data->instance_name, PID_EXTENSION))) checkers_pidfile.free_path = true; #endif #ifdef _WITH_VRRP_ if (!vrrp_pidfile.path && (vrrp_pidfile.path = make_pidfile_name(KEEPALIVED_PID_DIR VRRP_PID_FILE, global_data->instance_name, PID_EXTENSION))) vrrp_pidfile.free_path = true; #endif #ifdef _WITH_BFD_ if (!bfd_pidfile.path && (bfd_pidfile.path = make_pidfile_name(KEEPALIVED_PID_DIR BFD_PID_FILE, global_data->instance_name, PID_EXTENSION))) bfd_pidfile.free_path = true; #endif } if (use_pid_dir) { if (!main_pidfile.path) main_pidfile.path = KEEPALIVED_PID_DIR KEEPALIVED_PID_FILE PID_EXTENSION; #ifdef _WITH_LVS_ if (!checkers_pidfile.path) checkers_pidfile.path = KEEPALIVED_PID_DIR CHECKERS_PID_FILE PID_EXTENSION; #endif #ifdef _WITH_VRRP_ if (!vrrp_pidfile.path) vrrp_pidfile.path = KEEPALIVED_PID_DIR VRRP_PID_FILE PID_EXTENSION; #endif #ifdef _WITH_BFD_ if (!bfd_pidfile.path) bfd_pidfile.path = KEEPALIVED_PID_DIR BFD_PID_FILE PID_EXTENSION; #endif } else { if (!main_pidfile.path) main_pidfile.path = RUNSTATEDIR "/" KEEPALIVED_PID_FILE PID_EXTENSION; #ifdef _WITH_LVS_ if (!checkers_pidfile.path) checkers_pidfile.path = RUNSTATEDIR "/" CHECKERS_PID_FILE PID_EXTENSION; #endif #ifdef _WITH_VRRP_ if (!vrrp_pidfile.path) vrrp_pidfile.path = RUNSTATEDIR "/" VRRP_PID_FILE PID_EXTENSION; #endif #ifdef _WITH_BFD_ if (!bfd_pidfile.path) bfd_pidfile.path = RUNSTATEDIR "/" BFD_PID_FILE PID_EXTENSION; #endif } #ifndef _ONE_PROCESS_DEBUG_ /* We have set the namespaces, so we can do this now */ remove_reload_file(); #endif /* Check if keepalived is already running */ if (keepalived_running(daemon_mode)) { log_message(LOG_INFO, "daemon is already running"); report_stopped = false; goto end; } } /* daemonize process */ if (!__test_bit(DONT_FORK_BIT, &debug)) { pid_t old_ppid = getpid(); if (xdaemon() > 0) { /* Parent process */ closelog(); FREE_CONST_PTR(config_id); FREE_PTR(orig_core_dump_pattern); close_std_fd(); exit(0); } /* Child process */ /* check_start_stop_script_secure() called below makes a check * that our parent process hasn't changed, but it can take a while * for the parent of the fork to exit which is when our parent pid * changes. We need to loop until that has happened. */ while (old_ppid == getppid()) usleep(10); } #ifdef _MEM_CHECK_ enable_mem_log_termination(); #endif if (global_data->startup_script || global_data->shutdown_script) { /* This is a workaround for the check in check_start_stop_script_secure() */ main_pid = getppid(); magic = ka_magic_open(); script_flags = 0; if (global_data->startup_script) script_flags |= check_start_stop_script_secure(&global_data->startup_script, magic); if (global_data->shutdown_script) script_flags |= check_start_stop_script_secure(&global_data->shutdown_script, magic); if (magic) ka_magic_close(magic); if (!script_security && script_flags & SC_ISSCRIPT) { report_config_error(CONFIG_SECURITY_ERROR, "SECURITY VIOLATION - start/shutdown scripts are being executed but script_security not enabled.%s", script_flags & SC_INSECURE ? " There are insecure scripts." : ""); } } if (__test_bit(CONFIG_TEST_BIT, &debug)) { validate_config(); config_test_exit(); } /* write the father's pidfile */ if (!pidfile_write(&main_pidfile)) goto end; if (!global_data->max_auto_priority) log_message(LOG_INFO, "NOTICE: setting config option max_auto_priority should result in better keepalived performance"); /* Create the master thread */ master = thread_make_master(); /* Signal handling initialization */ signal_init(); #ifndef _ONE_PROCESS_DEBUG_ /* Open eventfd for children notifying parent that they have read the configuration file */ if (!__test_bit(CONFIG_TEST_BIT, &debug)) open_config_read_fd(); #endif /* If we have a startup script, run it first */ if (global_data->startup_script) { thread_add_event(master, run_startup_script, NULL, 0); } else { /* Init daemon */ thread_add_event(master, start_keepalived, NULL, 0); } initialise_debug_options(); #ifdef THREAD_DUMP register_parent_thread_addresses(); #endif /* Launch the scheduling I/O multiplexer */ exit_code = launch_thread_scheduler(master); /* Finish daemon process */ stop_keepalived(); #ifdef THREAD_DUMP deregister_thread_addresses(); #endif /* * Reached when terminate signal catched. * finally return from system */ end: if (report_stopped) { if (__test_bit(LOG_DETAIL_BIT, &debug)) { getrusage(RUSAGE_SELF, &usage); getrusage(RUSAGE_CHILDREN, &child_usage); log_message(LOG_INFO, "CPU usage (self/children) user: %" PRI_tv_sec ".%6.6" PRI_tv_usec "/%" PRI_tv_sec ".%6.6" PRI_tv_usec " system: %" PRI_tv_sec ".%6.6" PRI_tv_usec "/%" PRI_tv_sec ".%6.6" PRI_tv_usec, usage.ru_utime.tv_sec, usage.ru_utime.tv_usec, child_usage.ru_utime.tv_sec, child_usage.ru_utime.tv_usec, usage.ru_stime.tv_sec, usage.ru_stime.tv_usec, child_usage.ru_stime.tv_sec, child_usage.ru_stime.tv_usec); } #ifdef GIT_COMMIT log_message(LOG_INFO, "Stopped %s, git commit %s", version_string, GIT_COMMIT); #else log_message(LOG_INFO, "Stopped %s", version_string); #endif } if (global_data && global_data->network_namespace) clear_namespaces(); if (use_pid_dir) remove_pid_dir(); /* Restore original core_pattern if necessary */ if (orig_core_dump_pattern) update_core_dump_pattern(orig_core_dump_pattern); free_parent_mallocs_startup(false); free_parent_mallocs_exit(); free_global_data(&global_data); closelog(); #ifdef _MEM_CHECK_LOG_ if (syslog_ident) free(no_const_char_p(syslog_ident)); /* malloc'd in make_syslog_ident */ #else FREE_CONST_PTR(syslog_ident); #endif close_std_fd(); #ifdef _REPRODUCIBLE_BUILD_ FREE_CONST_PTR(config_opts); config_opts = "removed"; #endif return exit_code; } keepalived-2.3.3/keepalived/core/Makefile.in0000664000175000017500000005270614772274255014452 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2020 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : @SNMP_TRUE@am__append_1 = snmp.o @SNMP_TRUE@am__append_2 = snmp.c @NFTABLES_TRUE@am__append_3 = nftables.o @NFTABLES_TRUE@am__append_4 = nftables.c @LIBNL_DYNAMIC_TRUE@am__append_5 = libnl_link.o @LIBNL_DYNAMIC_TRUE@am__append_6 = libnl_link.c @TRACK_PROCESS_TRUE@am__append_7 = track_process.o @TRACK_PROCESS_TRUE@am__append_8 = track_process.c @ONE_PROCESS_DEBUG_FALSE@am__append_9 = reload_monitor.o config_notify.o @ONE_PROCESS_DEBUG_FALSE@am__append_10 = reload_monitor.c config_notify.c @WITH_SANITIZER_TRUE@am__append_11 = sanitizer.o @WITH_SANITIZER_TRUE@am__append_12 = sanitizer.c subdir = keepalived/core ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libcore_a_AR = $(AR) $(ARFLAGS) libcore_a_DEPENDENCIES = $(am__append_1) $(am__append_3) \ $(am__append_5) $(am__append_7) $(am__append_9) \ $(am__append_11) am_libcore_a_OBJECTS = main.$(OBJEXT) daemon.$(OBJEXT) \ pidfile.$(OBJEXT) layer4.$(OBJEXT) smtp.$(OBJEXT) \ global_data.$(OBJEXT) global_parser.$(OBJEXT) \ keepalived_netlink.$(OBJEXT) namespaces.$(OBJEXT) am__EXTRA_libcore_a_SOURCES_DIST = snmp.c nftables.c libnl_link.c \ track_process.c reload_monitor.c config_notify.c sanitizer.c libcore_a_OBJECTS = $(am_libcore_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/lib depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/config_notify.Po \ ./$(DEPDIR)/daemon.Po ./$(DEPDIR)/global_data.Po \ ./$(DEPDIR)/global_parser.Po ./$(DEPDIR)/keepalived_netlink.Po \ ./$(DEPDIR)/layer4.Po ./$(DEPDIR)/libnl_link.Po \ ./$(DEPDIR)/main.Po ./$(DEPDIR)/namespaces.Po \ ./$(DEPDIR)/nftables.Po ./$(DEPDIR)/pidfile.Po \ ./$(DEPDIR)/reload_monitor.Po ./$(DEPDIR)/sanitizer.Po \ ./$(DEPDIR)/smtp.Po ./$(DEPDIR)/snmp.Po \ ./$(DEPDIR)/track_process.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libcore_a_SOURCES) $(EXTRA_libcore_a_SOURCES) DIST_SOURCES = $(libcore_a_SOURCES) \ $(am__EXTRA_libcore_a_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/build-aux/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AM_CPPFLAGS = -I $(top_srcdir)/keepalived/include -I $(top_srcdir)/lib \ $(KA_CPPFLAGS) $(DEBUG_CPPFLAGS) \ -DLOCAL_STATE_DIR=\"@localstatedir@\" AM_CFLAGS = $(KA_CFLAGS) $(DEBUG_CFLAGS) AM_LDFLAGS = $(KA_LDFLAGS) $(DEBUG_LDFLAGS) # AM_LIBS = $(KA_LIBS) # AM_LIBTOOLFLAGS = $(KA_LIBTOOLFLAGS) noinst_LIBRARIES = libcore.a libcore_a_SOURCES = main.c daemon.c pidfile.c layer4.c smtp.c \ global_data.c global_parser.c keepalived_netlink.c \ namespaces.c libcore_a_LIBADD = $(am__append_1) $(am__append_3) $(am__append_5) \ $(am__append_7) $(am__append_9) $(am__append_11) EXTRA_libcore_a_SOURCES = $(am__append_2) $(am__append_4) \ $(am__append_6) $(am__append_8) $(am__append_10) \ $(am__append_12) all: all-am .SUFFIXES: .SUFFIXES: .c .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign keepalived/core/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign keepalived/core/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libcore.a: $(libcore_a_OBJECTS) $(libcore_a_DEPENDENCIES) $(EXTRA_libcore_a_DEPENDENCIES) $(AM_V_at)-rm -f libcore.a $(AM_V_AR)$(libcore_a_AR) libcore.a $(libcore_a_OBJECTS) $(libcore_a_LIBADD) $(AM_V_at)$(RANLIB) libcore.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config_notify.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/global_data.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/global_parser.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keepalived_netlink.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layer4.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnl_link.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/namespaces.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nftables.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pidfile.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reload_monitor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/track_process.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/config_notify.Po -rm -f ./$(DEPDIR)/daemon.Po -rm -f ./$(DEPDIR)/global_data.Po -rm -f ./$(DEPDIR)/global_parser.Po -rm -f ./$(DEPDIR)/keepalived_netlink.Po -rm -f ./$(DEPDIR)/layer4.Po -rm -f ./$(DEPDIR)/libnl_link.Po -rm -f ./$(DEPDIR)/main.Po -rm -f ./$(DEPDIR)/namespaces.Po -rm -f ./$(DEPDIR)/nftables.Po -rm -f ./$(DEPDIR)/pidfile.Po -rm -f ./$(DEPDIR)/reload_monitor.Po -rm -f ./$(DEPDIR)/sanitizer.Po -rm -f ./$(DEPDIR)/smtp.Po -rm -f ./$(DEPDIR)/snmp.Po -rm -f ./$(DEPDIR)/track_process.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/config_notify.Po -rm -f ./$(DEPDIR)/daemon.Po -rm -f ./$(DEPDIR)/global_data.Po -rm -f ./$(DEPDIR)/global_parser.Po -rm -f ./$(DEPDIR)/keepalived_netlink.Po -rm -f ./$(DEPDIR)/layer4.Po -rm -f ./$(DEPDIR)/libnl_link.Po -rm -f ./$(DEPDIR)/main.Po -rm -f ./$(DEPDIR)/namespaces.Po -rm -f ./$(DEPDIR)/nftables.Po -rm -f ./$(DEPDIR)/pidfile.Po -rm -f ./$(DEPDIR)/reload_monitor.Po -rm -f ./$(DEPDIR)/sanitizer.Po -rm -f ./$(DEPDIR)/smtp.Po -rm -f ./$(DEPDIR)/snmp.Po -rm -f ./$(DEPDIR)/track_process.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ clean-generic clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/keepalived/core/track_process.c0000664000175000017500000011300514640507763015376 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Process tracking framework. * * Author: Alexandre Cassen, * * 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) 2018-2018 Alexandre Cassen, */ /* For details on the proc connector, see: * * https://stackoverflow.com/questions/26852228/detect-new-process-creation-instantly-in-linux * https://unix.stackexchange.com/questions/260162/how-to-track-newly-created-processes-in-linux * http://netsplit.com/the-proc-connector-and-socket-filters */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "track_process.h" #include "global_data.h" #include "list_head.h" #include "rbtree_ka.h" #include "vrrp_data.h" #include "utils.h" #include "bitops.h" #include "logger.h" #include "main.h" #include "process.h" #include "align.h" static thread_ref_t read_thread; static thread_ref_t reload_thread; static rb_root_t process_tree = RB_ROOT; static int nl_sock = -1; static unsigned num_cpus; static int64_t *cpu_seq; static bool need_reinitialise; bool proc_events_not_supported; bool proc_events_responded; #ifdef _TRACK_PROCESS_DEBUG_ bool do_track_process_debug; bool do_track_process_debug_detail; #endif #ifdef _INCLUDE_UNUSED_CODE_ static void dump_process_tree(const char *str) { tracked_process_instance_t *tpi; ref_tracked_process_t *rtpr; log_message(LOG_INFO, "Process tree - %s", str); rb_for_each_entry(tpi, &process_tree, pid_tree) { log_message(LOG_INFO, "Pid %d", tpi->pid); list_for_each_entry(rtpr, &tpi->processes, e_list) log_message(LOG_INFO, " %s", rtpr->process->pname); } } #endif static void set_rcv_buf(unsigned buf_size, bool force) { if (setsockopt(nl_sock, SOL_SOCKET, force ? SO_RCVBUFFORCE : SO_RCVBUF, &buf_size, sizeof(buf_size)) < 0) log_message(LOG_INFO, "Cannot set process monitor SO_RCVBUF%s option. errno=%d (%m)" , force ? "FORCE" : "", errno); } static void free_ref_tracked_process(ref_tracked_process_t *rtpr) { list_del_init(&rtpr->e_list); FREE(rtpr); } static void free_ref_tracked_process_list(list_head_t *l) { ref_tracked_process_t *rtpr, *rtpr_tmp; list_for_each_entry_safe(rtpr, rtpr_tmp, l, e_list) free_ref_tracked_process(rtpr); } static ref_tracked_process_t * alloc_ref_tracked_process(vrrp_tracked_process_t *tpr, tracked_process_instance_t *tpi) { ref_tracked_process_t *new; PMALLOC(new); INIT_LIST_HEAD(&new->e_list); new->process = tpr; list_add_tail(&new->e_list, &tpi->processes); return new; } static void free_tracked_process_instance(tracked_process_instance_t *tpi) { free_ref_tracked_process_list(&tpi->processes); rb_erase(&tpi->pid_tree, &process_tree); FREE(tpi); } static void free_process_tree(void) { tracked_process_instance_t *tpi, *next; rbtree_postorder_for_each_entry_safe(tpi, next, &process_tree, pid_tree) { free_ref_tracked_process_list(&tpi->processes); FREE(tpi); } process_tree = RB_ROOT; } static int pid_compare(const void *pid, const rb_node_t *a) { return less_equal_greater_than(*PTR_CAST_CONST(pid_t, pid), rb_entry_const(a, tracked_process_instance_t, pid_tree)->pid); } static bool pid_less(rb_node_t *a, const rb_node_t *b) { return rb_entry(a, tracked_process_instance_t, pid_tree)->pid < rb_entry_const(b, tracked_process_instance_t, pid_tree)->pid; } static inline tracked_process_instance_t * alloc_tracked_process_instance(pid_t pid) { tracked_process_instance_t *new; PMALLOC(new); INIT_LIST_HEAD(&new->processes); new->pid = pid; RB_CLEAR_NODE(&new->pid_tree); rb_add(&new->pid_tree, &process_tree, pid_less); return new; } static inline tracked_process_instance_t * add_process(pid_t pid, vrrp_tracked_process_t *tpr, tracked_process_instance_t *tpi) { rb_node_t *tpi_node; if (!tpi) { if ((tpi_node = rb_find(&pid, &process_tree, pid_compare))) tpi = rb_entry(tpi_node, tracked_process_instance_t, pid_tree); else tpi = alloc_tracked_process_instance(pid); } alloc_ref_tracked_process(tpr, tpi); ++tpr->num_cur_proc; return tpi; } #ifdef _INCLUDE_UNUSED_CODE_ static int scandir_filter(const struct dirent *dirent) { if (dirent->d_type != DT_DIR) return false; if (dirent->d_name[0] <= '0' || dirent->d_name[0] > '9') return false; return true; } static int scandir_sort(const struct dirent **a, const struct dirent **b) { return 0; } static pid_t read_procs(const char *name) { struct dirent **namelist; struct dirent **ent_p; struct dirent *ent; int ret; ret = scandir("/proc", &namelist, scandir_filter, scandir_sort); log_message(LOG_INFO, "scandir returned %d\n", ret); for (ent_p = namelist, ent = *namelist; ret--; ent = *++ent_p) { log_message(LOG_INFO, "0x%p: %s\n", ent, ent->d_name); free(ent); /* malloc'd by scandir() */ } free(namelist); /* malloc'd by scandir() */ } #endif static bool check_params(vrrp_tracked_process_t *tpr, const char *params, size_t params_len) { if (tpr->param_match == PARAM_MATCH_EXACT && !tpr->process_params && params_len == 0) return true; if (params_len < tpr->process_params_len) return false; if (tpr->param_match == PARAM_MATCH_EXACT) return (params_len == tpr->process_params_len && (!tpr->process_params || !memcmp(params, tpr->process_params, tpr->process_params_len))); if (!tpr->process_params) return true; if (tpr->param_match == PARAM_MATCH_PARTIAL) return !memcmp(params, tpr->process_params, tpr->process_params_len); /* tpr->param_match == PARAM_MATCH_INITIAL */ return tpr->process_params_len == 1 || !memcmp(params, tpr->process_params, tpr->process_params_len - 1); } static void read_procs(list_head_t *processes) { /* /proc/PID/status has line State: which can be Z for zombie process (but cmdline is empty then) * /proc/PID/stat has cmd name as 2nd field in (), and state as third field. For states see * man proc(5) * pgrep uses status and cmdline files. Without -f, pgrep looks at comm (in status/stat/comm). If * -f is specified, it reads cmdline. * To change comm for a process, use prctl(PR_SET_NAME). */ DIR *proc_dir = opendir("/proc"); struct dirent *ent; char cmdline[1 + 4 + 1 + PID_MAX_DIGITS + 1 + 7 + 1]; /* "/proc/xxxxxxx/cmdline" */ int fd; char *cmd_buf; size_t cmd_buf_len; char stat_buf[128]; char *p; char *comm; ssize_t len = 0; ssize_t cmdline_len = 0; char *proc_name; vrrp_tracked_process_t *tpr; const char *param_start; cmd_buf_len = vrrp_data->vrrp_max_process_name_len + 2; cmd_buf = MALLOC(cmd_buf_len); while ((ent = readdir(proc_dir))) { if (ent->d_type != DT_DIR) continue; if (ent->d_name[0] <= '0' || ent->d_name[0] > '9') continue; /* We want to avoid reading /proc/PID/cmdline, since it reads the process * address space, and if the process is swapped out, then it will have to be * swapped in to read it. */ if (vrrp_data->vrrp_use_process_cmdline) { snprintf(cmdline, sizeof(cmdline), "/proc/%.*s/cmdline", PID_MAX_DIGITS, ent->d_name); if ((fd = open(cmdline, O_RDONLY)) == -1) continue; /* Read max name len + null byte + 1 extra char */ cmdline_len = read(fd, cmd_buf, vrrp_data->vrrp_max_process_name_len + 1); close(fd); if (cmdline_len < 0) continue; cmd_buf[cmdline_len] = '\0'; } if (vrrp_data->vrrp_use_process_comm) { snprintf(cmdline, sizeof(cmdline), "/proc/%.*s/stat", PID_MAX_DIGITS, ent->d_name); if ((fd = open(cmdline, O_RDONLY)) == -1) continue; len = read(fd, stat_buf, sizeof(stat_buf) - 1); close(fd); if (len < 0) continue; stat_buf[len] = '\0'; if (len && stat_buf[len-1] == '\n') stat_buf[len - 1] = '\0'; /* Find the comm field, terminate it and check not a zombie process */ p = strchr(stat_buf + 2, '('); if (!p) continue; comm = p + 1; p = strchr(p, ')'); if (!p) continue; *p = '\0'; if (p[2] == 'Z') continue; } else comm = NULL; /* Avoid compiler warning */ list_for_each_entry(tpr, processes, e_list) { if (tpr->full_command) proc_name = cmd_buf; else if (comm) proc_name = comm; else /* This should never happen, but coverity produces a "Explicit null dereference" error */ continue; if (!strcmp(proc_name, tpr->process_path)) { /* We have got a match */ /* Do we need to check parameters? */ if (tpr->param_match != PARAM_MATCH_NONE) { param_start = proc_name + strlen(proc_name) + 1; if (!check_params(tpr, param_start, proc_name + cmdline_len - param_start)) continue; } add_process(atoi(ent->d_name), tpr, NULL); } } } closedir(proc_dir); FREE(cmd_buf); } static void update_process_status(vrrp_tracked_process_t *tpr, bool now_up) { if (now_up == tpr->have_quorum) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "update process status no change for %s", tpr->pname); #endif return; } tpr->have_quorum = now_up; process_update_track_process_status(tpr, now_up); } static void remove_process_from_track(tracked_process_instance_t *tpi, vrrp_tracked_process_t *tpr) { ref_tracked_process_t *rtpr, *rtpr_tmp; list_for_each_entry_safe(rtpr, rtpr_tmp, &tpi->processes, e_list) { if (rtpr->process == tpr) { free_ref_tracked_process(rtpr); if (tpr->num_cur_proc-- == tpr->quorum || tpr->num_cur_proc == tpr->quorum_max) { if (tpr->fork_timer_thread) { thread_cancel(tpr->fork_timer_thread); tpr->fork_timer_thread = NULL; } update_process_status(tpr, tpr->num_cur_proc == tpr->quorum_max); } return; } } } static void check_process(pid_t pid, char *comm, tracked_process_instance_t *tpi) { char cmdline[1 + 4 + 1 + PID_MAX_DIGITS + 1 + 7 + 1]; /* "/proc/xxxxxxx/{cmdline,comm}" */ int fd; char *cmd_buf = NULL; size_t cmd_buf_len; char comm_buf[17]; ssize_t len = 0; ssize_t cmdline_len = 0; char *proc_name; const char *param_start; vrrp_tracked_process_t *tpr; bool had_process; rb_node_t *tpi_node; bool have_comm = !!comm; #ifdef _TRACK_PROCESS_DEBUG_ int sav_errno; #endif /* Are we counting this process now? */ if (!tpi) { if ((tpi_node = rb_find(&pid, &process_tree, pid_compare))) tpi = rb_entry(tpi_node, tracked_process_instance_t, pid_tree); } had_process = !!tpi; /* We want to avoid reading /proc/PID/cmdline, since it reads the process * address space, and if the process is swapped out, then it will have to be * swapped in to read it. */ if (!have_comm) { if (vrrp_data->vrrp_use_process_cmdline) { snprintf(cmdline, sizeof(cmdline), "/proc/%d/cmdline", pid); if ((fd = open(cmdline, O_RDONLY)) == -1) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process failed to open %s, errno %d", cmdline, errno); #endif return; } cmd_buf_len = vrrp_data->vrrp_max_process_name_len + 3; cmd_buf = MALLOC(cmd_buf_len); cmdline_len = read(fd, cmd_buf, vrrp_data->vrrp_max_process_name_len + 2); #ifdef _TRACK_PROCESS_DEBUG_ sav_errno = errno; #endif close(fd); if (cmdline_len < 0) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process failed to read %s, errno %d", cmdline, sav_errno); #endif FREE(cmd_buf); return; } cmd_buf[cmdline_len] = '\0'; } if (vrrp_data->vrrp_use_process_comm) { snprintf(cmdline, sizeof(cmdline), "/proc/%d/comm", pid); if ((fd = open(cmdline, O_RDONLY)) == -1) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process failed to open %s, errno %d", cmdline, errno); #endif FREE_PTR(cmd_buf); return; } len = read(fd, comm_buf, sizeof(comm_buf) - 1); #ifdef _TRACK_PROCESS_DEBUG_ sav_errno = errno; #endif close(fd); if (len < 0) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process failed to read %s, errno %d", cmdline, sav_errno); #endif FREE_PTR(cmd_buf); return; } comm_buf[len] = '\0'; if (len && comm_buf[len-1] == '\n') comm_buf[len-1] = '\0'; comm = comm_buf; } } #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process %s (cmdline %s)", comm, cmd_buf ? cmd_buf : "[none]"); #endif list_for_each_entry(tpr, &vrrp_data->vrrp_track_processes, e_list) { if (tpr->full_command) { /* If this is a PROC_EVENT_COMM, we aren't dealing with the command line */ if (have_comm) continue; proc_name = cmd_buf; } else if (comm) proc_name = comm; else /* This should never happen, but coverity produces a "Dereference after null check" error */ continue; if (!strcmp(proc_name, tpr->process_path)) { /* We have got a match */ /* Do we need to check parameters? */ if (tpr->param_match != PARAM_MATCH_NONE) { param_start = proc_name + strlen(proc_name) + 1; if (!check_params(tpr, param_start, proc_name + cmdline_len - param_start)) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process parameter mis-match"); #endif if (had_process) remove_process_from_track(tpi, tpr); continue; } } tpi = add_process(pid, tpr, tpi); #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process adding process %d to %s", pid, tpr->pname); #endif if (tpr->num_cur_proc == tpr->quorum || tpr->num_cur_proc == tpr->quorum_max + 1) { /* Cancel terminate timer thread if any, otherwise update status */ #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process %s num_proc now %u, quorum [%u:%u]", tpr->pname, tpr->num_cur_proc, tpr->quorum, tpr->quorum_max); #endif if (tpr->terminate_timer_thread) { thread_cancel(tpr->terminate_timer_thread); tpr->terminate_timer_thread = NULL; } update_process_status(tpr, tpr->num_cur_proc == tpr->quorum); } } else if (had_process && !have_comm) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "check_process removing %d from %s", pid, tpr->pname); #endif remove_process_from_track(tpi, tpr); } } FREE_PTR(cmd_buf); if (!tpi) return; /* If we were monitoring the process, and are no longer, * remove it */ if (list_empty(&tpi->processes)) free_tracked_process_instance(tpi); } static void process_gained_quorum_timer_thread(thread_ref_t thread) { vrrp_tracked_process_t *tpr = thread->arg; #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "quorum gained timer for %s expired", tpr->pname); #endif update_process_status(tpr, tpr->num_cur_proc >= tpr->quorum && tpr->num_cur_proc <= tpr->quorum_max); tpr->fork_timer_thread = NULL; } static void check_process_fork(pid_t parent_pid, pid_t child_pid) { tracked_process_instance_t *tpi, *tpi_child; rb_node_t *tpi_node; vrrp_tracked_process_t *tpr; ref_tracked_process_t *rtpr; /* If we aren't interested in the parent, we aren't interested in the child */ if (!(tpi_node = rb_find(&parent_pid, &process_tree, pid_compare))) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "Ignoring fork for untracked pid %d", parent_pid); #endif return; } tpi = rb_entry(tpi_node, tracked_process_instance_t, pid_tree); tpi_child = alloc_tracked_process_instance(child_pid); #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "Adding new child %d of parent %d", child_pid, parent_pid); #endif list_for_each_entry(rtpr, &tpi->processes, e_list) { tpr = rtpr->process; #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "Adding new child %d to track_process %s", child_pid, tpr->pname); #endif /* Add a new reference */ alloc_ref_tracked_process(tpr, tpi_child); if (++tpr->num_cur_proc == tpr->quorum || tpr->num_cur_proc == tpr->quorum_max + 1) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "track_process %s num_proc now %u, quorum [%u:%u]", tpr->pname, tpr->num_cur_proc, tpr->quorum, tpr->quorum_max); #endif if (tpr->terminate_timer_thread) { thread_cancel(tpr->terminate_timer_thread); // Cancel terminate timer tpr->terminate_timer_thread = NULL; } else if (tpr->fork_delay) { tpr->fork_timer_thread = thread_add_timer(master, process_gained_quorum_timer_thread, tpr, tpr->fork_delay); #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "Adding timer %d for %s up", tpr->fork_delay, tpr->pname); #endif continue; } update_process_status(tpr, tpr->num_cur_proc == tpr->quorum); } } } static void process_lost_quorum_timer_thread(thread_ref_t thread) { vrrp_tracked_process_t *tpr = thread->arg; #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "quorum lost timer for %s expired", tpr->pname); #endif update_process_status(tpr, tpr->num_cur_proc >= tpr->quorum && tpr->num_cur_proc <= tpr->quorum_max); tpr->terminate_timer_thread = NULL; } static void check_process_termination(pid_t pid) { tracked_process_instance_t *tpi; rb_node_t *tpi_node; vrrp_tracked_process_t *tpr; ref_tracked_process_t *rtpr; tpi_node = rb_find(&pid, &process_tree, pid_compare); if (!tpi_node) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "Ignoring exit of untracked pid %d", pid); #endif return; } tpi = rb_entry(tpi_node, tracked_process_instance_t, pid_tree); list_for_each_entry(rtpr, &tpi->processes, e_list) { tpr = rtpr->process; if (tpr->num_cur_proc-- == tpr->quorum || tpr->num_cur_proc == tpr->quorum_max) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "process exit %s num_proc now %u, quorum [%u:%u]", tpr->pname, tpr->num_cur_proc, tpr->quorum, tpr->quorum_max); #endif if (tpr->fork_timer_thread) { thread_cancel(tpr->fork_timer_thread); // Cancel fork timer tpr->fork_timer_thread = NULL; } else if (tpr->terminate_delay) { tpr->terminate_timer_thread = thread_add_timer(master, process_lost_quorum_timer_thread, tpr, tpr->terminate_delay); #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "Adding timer %d for %s termination", tpr->terminate_delay, tpr->pname); #endif continue; } update_process_status(tpr, tpr->num_cur_proc == tpr->quorum_max); } } free_tracked_process_instance(tpi); } static void check_process_comm_change(pid_t pid, char *comm) { tracked_process_instance_t *tpi; rb_node_t *tpi_node; vrrp_tracked_process_t *tpr; ref_tracked_process_t *rtpr, *rtpr_tmp; tpi_node = rb_find(&pid, &process_tree, pid_compare); if (!tpi_node) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "comm_change pid %d not found", pid); #endif tpi = NULL; goto end; } tpi = rb_entry(tpi_node, tracked_process_instance_t, pid_tree); /* The process was being monitored by its old name */ list_for_each_entry_safe(rtpr, rtpr_tmp, &tpi->processes, e_list) { tpr = rtpr->process; if (tpr->full_command) continue; /* Check that the name really has changed */ if (!strcmp(comm, tpr->process_path)) return; #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "comm change remove pid %d", pid); #endif free_ref_tracked_process(rtpr); if (tpr->num_cur_proc-- == tpr->quorum || tpr->num_cur_proc == tpr->quorum_max) { #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug_detail) log_message(LOG_INFO, "comm change %s num_proc now %u, quorum [%u:%u]" , tpr->pname, tpr->num_cur_proc , tpr->quorum, tpr->quorum_max); #endif if (tpr->fork_timer_thread) { thread_cancel(tpr->fork_timer_thread); // Cancel fork timer tpr->fork_timer_thread = NULL; } update_process_status(tpr, tpr->num_cur_proc == tpr->quorum_max); } } end: /* Handle the new process name */ check_process(pid, comm, tpi); } /* * connect to netlink * returns netlink socket, or -1 on error */ static int nl_connect(void) { int rc; int nl_sd; struct sockaddr_nl sa_nl; nl_sd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, NETLINK_CONNECTOR); if (nl_sd == -1) { if (errno == EPROTONOSUPPORT) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "track_process not available - is CONFIG_PROC_EVENTS enabled in kernel config?"); proc_events_not_supported = true; } else log_message(LOG_INFO, "Failed to open process monitoring socket - errno %d - %m", errno); return -1; } sa_nl.nl_family = AF_NETLINK; sa_nl.nl_groups = CN_IDX_PROC; sa_nl.nl_pid = getpid(); rc = bind(nl_sd, PTR_CAST(struct sockaddr, &sa_nl), sizeof(sa_nl)); if (rc == -1) { log_message(LOG_INFO, "Failed to bind to process monitoring socket - errno %d - %m", errno); close(nl_sd); return -1; } return nl_sd; } /* * subscribe on proc events (process notifications) */ static int set_proc_ev_listen(int nl_sd, bool enable) { int rc; struct __attribute__ ((aligned(NLMSG_ALIGNTO))) { struct nlmsghdr nl_hdr; struct __attribute__ ((__packed__)) { struct cn_msg cn_msg; enum proc_cn_mcast_op cn_mcast; }; } nlcn_msg; memset(&nlcn_msg, 0, sizeof(nlcn_msg)); nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg); nlcn_msg.nl_hdr.nlmsg_pid = getpid(); nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE; nlcn_msg.cn_msg.id.idx = CN_IDX_PROC; nlcn_msg.cn_msg.id.val = CN_VAL_PROC; nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op); nlcn_msg.cn_mcast = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE; rc = send(nl_sd, &nlcn_msg, sizeof(nlcn_msg), 0); if (rc == -1) { log_message(LOG_INFO, "Failed to %s process event listen - errno %d - %m", enable ? "set" : "clear", errno); return -1; } return 0; } static void reinitialise_track_processes(void) { reload_thread = NULL; unsigned buf_size; socklen_t buf_size_len = sizeof(buf_size); unsigned i; vrrp_tracked_process_t *tpr; need_reinitialise = false; if (getsockopt(nl_sock, SOL_SOCKET, SO_RCVBUF, &buf_size, &buf_size_len) < 0) { log_message(LOG_INFO, "Cannot get process monitor SO_RCVBUF option. errno=%d (%m)", errno); return; } buf_size *= 2; set_rcv_buf(buf_size, global_data->process_monitor_rcv_bufs_force); log_message(LOG_INFO, "Setting global_def process_monitor_rcv_bufs to %u" " - recommend updating configuration file" , buf_size); /* Reset the sequence numbers */ for (i = 0; i < num_cpus; i++) cpu_seq[i] = -1; /* Remove the existing process tree */ free_process_tree(); /* Save process counters, and clear any down timers */ list_for_each_entry(tpr, &vrrp_data->vrrp_track_processes, e_list) { tpr->sav_num_cur_proc = tpr->num_cur_proc; tpr->num_cur_proc = 0; if (tpr->fork_timer_thread) { thread_cancel(tpr->fork_timer_thread); tpr->fork_timer_thread = NULL; } if (tpr->terminate_timer_thread) { thread_cancel(tpr->terminate_timer_thread); tpr->terminate_timer_thread = NULL; } } /* Re read processes */ read_procs(&vrrp_data->vrrp_track_processes); /* See if anything changed */ list_for_each_entry(tpr, &vrrp_data->vrrp_track_processes, e_list) { if (tpr->sav_num_cur_proc != tpr->num_cur_proc) { if ((tpr->sav_num_cur_proc < tpr->quorum) == (tpr->num_cur_proc < tpr->quorum) && (tpr->sav_num_cur_proc > tpr->quorum_max) == (tpr->num_cur_proc > tpr->quorum_max)) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Process %s, number of current processes changed" " from %u to %u" , tpr->pname , tpr->sav_num_cur_proc , tpr->num_cur_proc); continue; } if (tpr->num_cur_proc >= tpr->quorum && tpr->num_cur_proc <= tpr->quorum_max) { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Process %s, number of current processes changed" " from %u to %u, quorum up" , tpr->pname , tpr->sav_num_cur_proc , tpr->num_cur_proc); if (tpr->fork_delay) tpr->fork_timer_thread = thread_add_timer(master, process_gained_quorum_timer_thread, tpr, tpr->fork_delay); else process_update_track_process_status(tpr, true); } else { if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "Process %s, number of current processes changed" " from %u to %u, quorum down" , tpr->pname , tpr->sav_num_cur_proc , tpr->num_cur_proc); if (tpr->terminate_delay) tpr->terminate_timer_thread = thread_add_timer(master, process_lost_quorum_timer_thread, tpr, tpr->terminate_delay); else process_update_track_process_status(tpr, false); } } } } static void process_lost_messages_timer_thread(__attribute__((unused)) thread_ref_t thread) { reinitialise_track_processes(); } /* * handle a single process event * * There is a ?design bug in the kernel. struct proc_event has 8 byte alignment, * but struct nlmsghdr is 16 bytes long, the payload is then 4 byte aligned, which * starts with a struct cn_msg which is 20 bytes long and is immediately followed by * the struct proc_event. This means that if the buffer for the data is 8 byte * aligned, then proc_event ends up 4 byte aligned but NOT 8 byte aligned. * * A consequence of the above is that there cannot be multiple chained netlink * messages in one receive block, since if the first proc_event is 8 byte aligned, * the second one will not be 8 byte aligned. * * The kernel, in drivers/connector/cn_proc.c, allocates an 8 byte aligned buffer * and then start building the packet at a 4 byte offset into the buffer in order * to work around the problem. * * The normal approach of a loop for receiving netlink messages: * * for (nlmsghdr = (struct nlmsghdr *)buf; * NLMSG_OK (nlmsghdr, len); nlmsghdr = NLMSG_NEXT (nlmsghdr, len)) { * * will not work while maintaining 8 byte alignment of the proc_event structures. * However, the kernel does not send chained proc_event messages currently, and can't * without the alignment problem being resolved, so it should be safe to rely on that. * * For receiving, we can either use the kernel's approach of allocating an 8 byte * aligned buffer and receive at an offset of 4 bytes, or alternatively, as we have * chosen to do, use a scatter read. * */ static int handle_proc_ev(int nl_sd) { ssize_t len; struct sockaddr_nl addr; union nlmsghdr_alignment { struct nlmsghdr nlmsghdr; char dummy[NLMSG_ALIGN(sizeof(struct nlmsghdr))]; } u; struct cn_msg cn_msg; struct proc_event proc_ev; struct iovec iov[3] = { { &u, sizeof(u) }, { &cn_msg, sizeof(struct cn_msg) }, { &proc_ev, sizeof(struct proc_event) } }; struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 3 }; msg.msg_name = &addr; while (msg.msg_namelen = sizeof(addr), (len = recvmsg(nl_sd, &msg, 0))) { if (len == -1) { if (check_EINTR(errno)) continue; if (check_EAGAIN(errno)) return 0; if (errno == ENOBUFS) { /* We have missed some messages. Allow time for * things to settle down, and reinitialise. */ if (reload_thread) thread_cancel(reload_thread); reload_thread = thread_add_timer(master, process_lost_messages_timer_thread, NULL, TIMER_HZ); need_reinitialise = true; } else log_message(LOG_INFO, "process monitor netlink recv error %d - %m", errno); return -1; } /* Ensure the message has been sent by the kernel */ if (msg.msg_namelen != sizeof(addr) || addr.nl_pid != 0) { log_message(LOG_INFO, "addrlen %u, expect %zu, pid %u", msg.msg_namelen, sizeof addr, addr.nl_pid); return -1; } if (!NLMSG_OK (&u.nlmsghdr, len)) { log_message(LOG_INFO, "proc_event !NLMSG_OK"); return -1; } if (u.nlmsghdr.nlmsg_type == NLMSG_ERROR || u.nlmsghdr.nlmsg_type == NLMSG_NOOP) continue; if (cn_msg.id.idx != CN_IDX_PROC || cn_msg.id.val != CN_VAL_PROC) continue; /* On 3.10 kernel, proc_ev->cpu can be UINT32_MAX */ if (proc_ev.cpu >= num_cpus) continue; /* PROC_EVENT_NONE is an ack, otherwise not an ack */ if ((proc_ev.what == PROC_EVENT_NONE) != cn_msg.ack) continue; if (cpu_seq) { if ((!need_reinitialise || __test_bit(LOG_DETAIL_BIT, &debug)) && cpu_seq[proc_ev.cpu] != -1 && !(cpu_seq[proc_ev.cpu] + 1 == cn_msg.seq || (cn_msg.seq == 0 && cpu_seq[proc_ev.cpu] == UINT32_MAX))) log_message(LOG_INFO, "Missed %" PRIi64 " messages on CPU %u", cn_msg.seq - cpu_seq[proc_ev.cpu] - 1, proc_ev.cpu); cpu_seq[proc_ev.cpu] = cn_msg.seq; } #ifdef _TRACK_PROCESS_DEBUG_ if (do_track_process_debug) { switch (proc_ev.what) { case PROC_EVENT_NONE: log_message(LOG_INFO, "set mcast listen ok"); break; case PROC_EVENT_FORK: /* See if we have parent pid, in which case this is a new process */ log_message(LOG_INFO, "fork: parent tid=%d pid=%d -> child tid=%d pid=%d", proc_ev.event_data.fork.parent_pid, proc_ev.event_data.fork.parent_tgid, proc_ev.event_data.fork.child_pid, proc_ev.event_data.fork.child_tgid); break; case PROC_EVENT_EXEC: log_message(LOG_INFO, "exec: tid=%d pid=%d", proc_ev.event_data.exec.process_pid, proc_ev.event_data.exec.process_tgid); break; case PROC_EVENT_UID: log_message(LOG_INFO, "uid change: tid=%d pid=%d from %" PRIu32 " to %" PRIu32, proc_ev.event_data.id.process_pid, proc_ev.event_data.id.process_tgid, proc_ev.event_data.id.r.ruid, proc_ev.event_data.id.e.euid); break; case PROC_EVENT_GID: log_message(LOG_INFO, "gid change: tid=%d pid=%d from %" PRIu32 " to %" PRIu32, proc_ev.event_data.id.process_pid, proc_ev.event_data.id.process_tgid, proc_ev.event_data.id.r.rgid, proc_ev.event_data.id.e.egid); break; case PROC_EVENT_SID: log_message(LOG_INFO, "sid change: tid=%d pid=%d", proc_ev.event_data.sid.process_pid, proc_ev.event_data.sid.process_tgid); break; case PROC_EVENT_PTRACE: log_message(LOG_INFO, "ptrace change: tid=%d pid=%d tracer tid=%d, pid=%d", proc_ev.event_data.ptrace.process_pid, proc_ev.event_data.ptrace.process_tgid, proc_ev.event_data.ptrace.tracer_pid, proc_ev.event_data.ptrace.tracer_tgid); break; case PROC_EVENT_COMM: log_message(LOG_INFO, "comm: tid=%d pid=%d comm %s", proc_ev.event_data.comm.process_pid, proc_ev.event_data.comm.process_tgid, proc_ev.event_data.comm.comm); break; case PROC_EVENT_COREDUMP: log_message(LOG_INFO, "coredump: tid=%d pid=%d", proc_ev.event_data.coredump.process_pid, proc_ev.event_data.coredump.process_tgid); break; case PROC_EVENT_EXIT: log_message(LOG_INFO, "exit: tid=%d pid=%d exit_code=%u, signal=%u,", proc_ev.event_data.exit.process_pid, proc_ev.event_data.exit.process_tgid, proc_ev.event_data.exit.exit_code, proc_ev.event_data.exit.exit_signal); break; default: log_message(LOG_INFO, "unhandled proc event %u", proc_ev.what); break; } } #endif switch (proc_ev.what) { case PROC_EVENT_NONE: proc_events_responded = true; if (__test_bit(LOG_DETAIL_BIT, &debug)) log_message(LOG_INFO, "proc_events has confirmed it is configured"); break; case PROC_EVENT_FORK: /* See if we have parent pid, in which case this is a new process. * For a process fork, child_pid == child_tgid. * For a new thread, child_pid != child_tgid and parent_pid/tgid is * the parent process of the process doing the pthread_create(). */ if (proc_ev.event_data.fork.child_tgid == proc_ev.event_data.fork.child_pid) check_process_fork(proc_ev.event_data.fork.parent_tgid, proc_ev.event_data.fork.child_tgid); #ifdef _TRACK_PROCESS_DEBUG_ else if (do_track_process_debug_detail) log_message(LOG_INFO, "Ignoring new thread %d for pid %d", proc_ev.event_data.fork.child_tgid, proc_ev.event_data.fork.child_pid); #endif break; case PROC_EVENT_EXEC: /* We may be losing a process. Check if have pid, and check new cmdline */ if (proc_ev.event_data.exec.process_tgid == proc_ev.event_data.exec.process_pid) check_process(proc_ev.event_data.exec.process_tgid, NULL, NULL); #ifdef _TRACK_PROCESS_DEBUG_ else if (do_track_process_debug_detail) log_message(LOG_INFO, "Ignoring exec of thread %d of pid %d", proc_ev.event_data.exec.process_tgid, proc_ev.event_data.exec.process_pid); #endif break; case PROC_EVENT_COMM: if (proc_ev.event_data.comm.process_tgid == proc_ev.event_data.comm.process_pid) check_process_comm_change(proc_ev.event_data.comm.process_tgid, proc_ev.event_data.comm.comm); #ifdef _TRACK_PROCESS_DEBUG_ else if (do_track_process_debug_detail) log_message(LOG_INFO, "Ignoring COMM event of thread %d of pid %d", proc_ev.event_data.comm.process_tgid, proc_ev.event_data.comm.process_pid); #endif break; case PROC_EVENT_EXIT: /* We aren't interested in thread termination */ if (proc_ev.event_data.exit.process_tgid == proc_ev.event_data.exit.process_pid) check_process_termination(proc_ev.event_data.exit.process_tgid); #ifdef _TRACK_PROCESS_DEBUG_ else if (do_track_process_debug_detail) log_message(LOG_INFO, "Ignoring exit of thread %d of pid %d", proc_ev.event_data.exit.process_tgid, proc_ev.event_data.exit.process_pid); #endif break; default: break; } #ifdef CHECK_ONLY_ONE_NLMSG struct nlmsghdr *next_nlh = NLMSG_NEXT(&u.nlmsghdr, len); if (NLMSG_OK(next_nlh, len)) log_message(LOG_INFO, "NLMSG_OK(next_nlh, len)) returns yes"); #endif } if (len == 0) log_message(LOG_INFO, "proc_event recvmsg returned 0"); return 0; } static void read_process_update(thread_ref_t thread) { handle_proc_ev(thread->u.f.fd); read_thread = thread_add_read(thread->master, read_process_update, NULL, thread->u.f.fd, TIMER_NEVER, 0); } static void proc_events_ack_timer_thread(__attribute__((unused)) thread_ref_t thread) { if (!proc_events_responded) log_message(LOG_INFO, "WARNING - the kernel does not support proc events - track_process will not work"); } bool open_track_processes(void) { if (nl_sock != -1) return false; nl_sock = nl_connect(); if (nl_sock == -1) return true ; return false; } bool close_track_processes(void) { if (nl_sock == -1) return true; close(nl_sock); nl_sock = -1; return false; } bool init_track_processes(list_head_t *processes) { int rc = EXIT_SUCCESS; unsigned i; long num; if (global_data->process_monitor_rcv_bufs) set_rcv_buf(global_data->process_monitor_rcv_bufs, global_data->process_monitor_rcv_bufs_force); rc = set_proc_ev_listen(nl_sock, true); if (rc == -1) { close(nl_sock); nl_sock = -1; return EXIT_FAILURE; } /* We get a PROC_EVENT_NONE if the proc_events_connector is built * into the kernel. We have to timeout not receiving a message to * know that proc evnets are not available. */ if (!proc_events_responded) thread_add_timer(master, proc_events_ack_timer_thread, NULL, TIMER_HZ / 10); if (!cpu_seq) { /* should we consider only ONLINE CPU ? */ num = sysconf(_SC_NPROCESSORS_CONF); if (num > 0) { num_cpus = num; cpu_seq = MALLOC(num_cpus * sizeof(*cpu_seq)); for (i = 0; i < num_cpus; i++) cpu_seq[i] = -1; } else log_message(LOG_INFO, "sysconf returned %ld CPUs" " - ignoring and won't track process event sequence numbers" , num); } read_procs(processes); read_thread = thread_add_read(master, read_process_update, NULL, nl_sock, TIMER_NEVER, 0); return rc; } void reload_track_processes(void) { /* Remove the existing process tree */ free_process_tree(); /* Re read processes */ read_procs(&vrrp_data->vrrp_track_processes); /* Add read thread */ read_thread = thread_add_read(master, read_process_update, NULL, nl_sock, TIMER_NEVER, 0); return; } void end_process_monitor(void) { vrrp_tracked_process_t *tpr; if (!cpu_seq) return; if (nl_sock != -1) { set_proc_ev_listen(nl_sock, false); if (read_thread) { thread_cancel(read_thread); read_thread = NULL; } close(nl_sock); nl_sock = -1; } FREE_PTR(cpu_seq); /* Cancel any timer threads */ list_for_each_entry(tpr, &vrrp_data->vrrp_track_processes, e_list) { if (tpr->fork_timer_thread) { thread_cancel(tpr->fork_timer_thread); tpr->fork_timer_thread = NULL; } if (tpr->terminate_timer_thread) { thread_cancel(tpr->terminate_timer_thread); tpr->terminate_timer_thread = NULL; } } /* Remove the existing process tree */ free_process_tree(); } #ifdef THREAD_DUMP void register_process_monitor_addresses(void) { register_thread_address("process_lost_quorum", process_lost_quorum_timer_thread); register_thread_address("process_lost_messages", process_lost_messages_timer_thread); register_thread_address("read_process_update", read_process_update); register_thread_address("proc_events_ack_timer", proc_events_ack_timer_thread); } #endif keepalived-2.3.3/keepalived/core/pidfile.c0000664000175000017500000001663314746375431014163 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: pidfile utility. * * Author: Alexandre Cassen, * * 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, */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "logger.h" #include "pidfile.h" #include "main.h" #include "bitops.h" #include "utils.h" #include "memory.h" const char *pid_directory = KEEPALIVED_PID_DIR; static bool pid_dir_created; /* Create the directory for non-standard pid files */ void create_pid_dir(void) { bool error; /* We want to create the PID directory with permissions rwxr-xr-x */ if (umask_val & (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) umask(umask_val & ~(S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)); error = mkdir(pid_directory, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && errno != EEXIST; /* Restore the default umask */ if (umask_val & (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) umask(umask_val); if (error) log_message(LOG_INFO, "Unable to create directory %s", pid_directory); else pid_dir_created = true; } void remove_pid_dir(void) { if (!pid_dir_created) return; if (rmdir(pid_directory) && errno != ENOTEMPTY && errno != EBUSY) log_message(LOG_INFO, "unlink of %s failed - error (%d) '%s'", pid_directory, errno, strerror(errno)); } char * make_pidfile_name(const char* start, const char* instance, const char* extn) { size_t len; char *name; len = strlen(start) + 1; if (instance) len += strlen(instance) + 1; if (extn) len += strlen(extn); name = MALLOC(len); if (!name) { log_message(LOG_INFO, "Unable to make pidfile name for %s", start); return NULL; } strcpy(name, start); if (instance) { strcat(name, "_"); strcat(name, instance); } if (extn) strcat(name, extn); return name; } void pidfile_close(pidfile_t *pidf, bool free_path) { if (pidf->fd == -1) return; close(pidf->fd); pidf->fd = -1; if (free_path && pidf->free_path) { FREE_CONST_PTR(pidf->path); pidf->path = NULL; pidf->free_path = false; } } /* Remove the running daemon pidfile */ void pidfile_rm(pidfile_t *pidf) { unlink(pidf->path); if (pidf->fd != -1) pidfile_close(pidf, true); } void close_other_pidfiles(void) { if (prog_type != PROG_TYPE_PARENT) pidfile_close(&main_pidfile, true); #ifdef _WITH_VRRP_ if (prog_type != PROG_TYPE_VRRP) pidfile_close(&vrrp_pidfile, true); #endif #ifdef _WITH_LVS_ if (prog_type != PROG_TYPE_CHECKER) pidfile_close(&checkers_pidfile, true); #endif #ifdef _WITH_BFD_ if (prog_type != PROG_TYPE_BFD) pidfile_close(&bfd_pidfile, true); #endif } /* return the daemon running state */ static bool create_pidfile(pidfile_t *pidf) { struct stat st, fd_st; int error; int ret; #if HAVE_DECL_F_OFD_SETLK == 1 struct flock fl = { .l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = 0 }; #endif for (;;) { /* We want to create the file with permissions rw-r--r-- */ if (umask_val & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) umask(umask_val & ~(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); while ((pidf->fd = open(pidf->path, O_NOFOLLOW | O_CREAT | O_WRONLY | O_NONBLOCK | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1 && errno == EINTR); error = errno; /* Restore the default umask */ if (umask_val & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) umask(umask_val); if (pidf->fd == -1) { errno = error; return true; } #if HAVE_DECL_F_OFD_SETLK == 1 fl.l_pid = 0; while ((ret = fcntl(pidf->fd, F_OFD_SETLK, &fl)) && errno == EINTR); if (ret) { if (errno == EAGAIN) log_message(LOG_INFO, "Another process has pid file %s locked", pidf->path); else log_message(LOG_INFO, "Locking pid file %s error %d - %m", pidf->path, errno); break; } #endif /* Make sure the file has not been removed/moved */ if (stat(pidf->path, &st)) { close(pidf->fd); pidf->fd = -1; continue; } if (fstat(pidf->fd, &fd_st)) { /* This should not happen since we have the file open */ break; } if (st.st_dev != fd_st.st_dev || st.st_ino != fd_st.st_ino) { /* A new file with the same name has been created */ close(pidf->fd); pidf->fd = -1; continue; } while ((ret = ftruncate(pidf->fd, 0)) && errno == EINTR); if (ret) { /* This should not happen */ break; } /* pid file is now opened, locked and 0 length */ return false; } if (pidf->fd != -1) { close(pidf->fd); pidf->fd = -1; } return true; } /* Return parent process daemon state */ bool keepalived_running(unsigned long mode) { if (create_pidfile(&main_pidfile)) return true; #ifdef _WITH_VRRP_ if (__test_bit(DAEMON_VRRP, &mode) && create_pidfile(&vrrp_pidfile)) return true; #endif #ifdef _WITH_LVS_ if (__test_bit(DAEMON_CHECKERS, &mode) && create_pidfile(&checkers_pidfile)) return true; #endif #ifdef _WITH_BFD_ if (__test_bit(DAEMON_BFD, &mode) && create_pidfile(&bfd_pidfile)) return true; #endif return false; } /* Create the running daemon pidfile */ bool pidfile_write(pidfile_t *pidf) { /* If keepalived originally started with no configuration for this process, * the process won't have originally been started, and the parent process * will not have created and opened a pid file. This means that pidf->fd * could be -1 after a reload. */ if (!children_started && pidf->fd == -1) return false; if (children_started) { struct stat statb, fstatb; /* There could be more error handling, but that will just * complicate the code for minimal benefit. */ if (stat(pidf->path, &statb)) { /* pidfile no longer exists */ if (pidf->fd != -1) close(pidf->fd); create_pidfile(pidf); } else { if (pidf->fd == -1 || fstat(pidf->fd, &fstatb) || statb.st_dev != fstatb.st_dev || statb.st_ino != fstatb.st_ino) { if (pidf->fd != -1) { /* The pidfile has been deleted and recreated. Open the new one. */ close(pidf->fd); } /* There is a window where the pid file could be deleted between us closing it and re-opening it, * so allow for creating the pidfile again. */ while ((pidf->fd = open(pidf->path, O_NOFOLLOW | O_CREAT | O_WRONLY | O_NONBLOCK | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1 && errno == EINTR); if (pidf->fd == -1) return false; } /* Since we may have already written to the pid file, * we need to reset the file offset and truncate the file. */ lseek(pidf->fd, 0, SEEK_SET); if (ftruncate(pidf->fd, 0)) log_message(LOG_INFO, "ftruncate error %d - %m", errno); } } dprintf(pidf->fd, "%d\n", getpid()); return true; } keepalived-2.3.3/keepalived/core/reload_monitor.c0000664000175000017500000002773214134544022015547 /* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: Scheduled reload handling. * * Author: Quentin Armitage * * 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) 2020-2020 Alexandre Cassen, */ #include "config.h" #include #include #include #include #include #include #include #include /* For kill */ #include #include #include "reload_monitor.h" #include "logger.h" #include "global_data.h" #include "scheduler.h" #include "utils.h" static int dir_wd, file_wd; static thread_ref_t inotify_thread; static thread_ref_t reload_timer_thread_p; static const char *file_name; #ifndef HAVE_TIMEGM static char tz_utc[] = "TZ=UTC"; static char *utc_env[] = { tz_utc, NULL}; #endif // #define RELOAD_DEBUG static void reload_timer_thread(__attribute__((unused)) thread_ref_t thread) { int inotify_fd = inotify_thread->u.f.fd; reload_timer_thread_p = NULL; /* We don't want to know that the file is being removed */ thread_cancel(inotify_thread); inotify_thread = NULL; close(inotify_fd); inotify_fd = -1; if (!global_data->reload_repeat || global_data->reload_date_specified) unlink(global_data->reload_time_file); kill(getpid(), SIGHUP); /* or reload_config(); */ return; } static char * format_time_t(char *str, size_t len, time_t t) { struct tm tm; localtime_r(&t, &tm); strftime(str, len, "%Y-%m-%d %H:%M:%S", &tm); return str; } #ifndef HAVE_TIMEGM static time_t timegm(struct tm *tm) { char **sav_env = environ; time_t t; environ = utc_env; t = mktime(tm); /* Restore previous settings */ environ = sav_env; tzset(); return t; } #endif inline static void cancel_reload(bool log) { char time_str[20]; if (!reload_timer_thread_p) return; thread_cancel(reload_timer_thread_p); reload_timer_thread_p = NULL; if (log && global_data->reload_time) log_message(LOG_INFO, "Cancelling reload scheduled for %s", format_time_t(time_str, sizeof(time_str), global_data->reload_time)); global_data->reload_time = 0; } static time_t parse_datetime(const char *timestr, bool *date_specified) { #ifdef RELOAD_DEBUG char buf[128]; #endif time_t now = time(NULL); time_t t; struct tm tm, tm1; size_t len; char *end; bool utc = false; len = strlen(timestr); if (len && timestr[len - 1] == 'Z') { utc = true; len--; } if (len != 8 && len != 17 && len != 19) { log_message(LOG_INFO, "Reload time %s format is incorrect - ignoring", timestr); return -1; } if (utc) gmtime_r(&now, &tm); else localtime_r(&now, &tm); end = strptime(timestr, len == 8 ? "%H:%M:%S" : len == 17 ? "%y-%m-%d %H:%M:%S" : "%Y-%m-%d %H:%M:%S", &tm); if (!end || end[utc ? 1 : 0]) { log_message(LOG_INFO, "Reload date/time %s invalid - ignoring", timestr); return -1; } #ifdef RELOAD_DEBUG log_message(LOG_INFO, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d %s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_isdst ? " DST" : ""); #endif tm.tm_isdst = -1; #ifdef RELOAD_DEBUG strftime(buf, sizeof(buf), "%c", &tm); log_message(LOG_INFO, "strf - %s", buf); #endif tm1 = tm; t = mktime(&tm1); if (tm.tm_year != tm1.tm_year || tm.tm_mon != tm1.tm_mon || tm.tm_mday != tm1.tm_mday || tm.tm_hour != tm1.tm_hour || tm.tm_min != tm1.tm_min || tm.tm_sec != tm1.tm_sec) { log_message(LOG_INFO, "Reload time %s is not valid - ignoring", timestr); return -1; } if (utc) t = timegm(&tm1); if (t <= now) { if (len == 8) { tm1.tm_mday++; t = utc ? timegm(&tm1) : mktime(&tm1); #ifdef RELOAD_DEBUG log_message(LOG_INFO, "Going to tomorrow"); #endif } else { log_message(LOG_INFO, "Reload time %s is in the past - ignoring", timestr); return -2; } } *date_specified = (len >= 17); #ifdef RELOAD_DEBUG localtime_r(&t, &tm1); strftime(buf, sizeof(buf), "%c", &tm1); log_message(LOG_INFO, "mktime - %s%s - %s", buf, tm1.tm_isdst ? " DST" : "", *tzname); #endif return t; } static void read_file(void) { FILE *fp = fopen(global_data->reload_time_file, "r"); size_t len; time_t reload_time; char time_buf[21]; char old_time_buf[20]; unsigned long delay; if (fp) { if (fgets(time_buf, sizeof(time_buf), fp)) { if ((len = strlen(time_buf)) && time_buf[len - 1] == '\n') time_buf[--len] = '\0'; } else { #ifdef RELOAD_DEBUG log_message(LOG_INFO, "fgets returned NULL"); #endif cancel_reload(true); fclose(fp); return; } fclose(fp); } else { cancel_reload(true); return; } reload_time = parse_datetime(time_buf, &global_data->reload_date_specified); if (reload_time <= -1) { if (reload_time == -1) log_message(LOG_INFO, "Invalid reload time '%s' specified - ignoring", time_buf); else if (reload_time == -2) log_message(LOG_INFO, "Reload date/time is in the past - ignoring"); cancel_reload(true); return; } if (reload_time != global_data->reload_time) { set_time_now(); delay = (reload_time - time_now.tv_sec) * TIMER_HZ - time_now.tv_usec; if (global_data->reload_time) format_time_t(old_time_buf, sizeof(old_time_buf), global_data->reload_time); if (reload_time) format_time_t(time_buf, sizeof(time_buf), reload_time); if (reload_timer_thread_p) { if (reload_time) { log_message(LOG_INFO, "Reload time updated from %s to %s", old_time_buf, time_buf); timer_thread_update_timeout(reload_timer_thread_p, delay); } else cancel_reload(true); } else { if (reload_time) { log_message(LOG_INFO, "Scheduling reload for %s", time_buf); reload_timer_thread_p = thread_add_timer(master, reload_timer_thread, NULL, delay); } else log_message(LOG_INFO, "Cancelling reload but no thread"); } global_data->reload_time = reload_time; } } static int watch_file(int fd) { int wd; #ifdef RELOAD_DEBUG log_message(LOG_INFO, "add watch for %s", global_data->reload_time_file); #endif if ((wd = inotify_add_watch(fd, global_data->reload_time_file, IN_CLOSE_WRITE)) == -1) { #ifdef RELOAD_DEBUG log_message(LOG_INFO, "inotify_add_watch(%s) failed - errno %d - %m", global_data->reload_time_file, errno); #endif return -1; } read_file(); return wd; } static void inotify_event_thread(thread_ref_t thread) { char buf[256] __attribute__((aligned(__alignof__(struct inotify_event)))); char *buf_ptr; struct inotify_event* event; ssize_t len; while (true) { if ((len = read(thread->u.f.fd, buf, sizeof(buf))) < (ssize_t)sizeof(struct inotify_event)) { if (len == -1) { if (!check_EAGAIN(errno)) log_message(LOG_INFO, "inotify read() returned error %d - %m", errno); // Look to see how handled elsewhere } else log_message(LOG_INFO, "inotify read() returned short length %zd", len); break; } #ifdef RELOAD_DEBUG log_message(LOG_INFO, "read returned %zd bytes", len); #endif for (buf_ptr = buf; buf_ptr < buf + len; buf_ptr += event->len + sizeof(struct inotify_event)) { event = PTR_CAST(struct inotify_event, buf_ptr); #ifdef RELOAD_DEBUG log_message(LOG_INFO, "File %s, wd %d, cookie %" PRIu32, event->len ? event->name : "[NONE]", event->wd, event->cookie); if (event->mask & IN_ACCESS) log_message(LOG_INFO, " IN_ACCESS"); if (event->mask & IN_ATTRIB) log_message(LOG_INFO, " IN_ATTRIB"); if (event->mask & IN_CLOSE_WRITE) log_message(LOG_INFO, " IN_CLOSE_WRITE"); if (event->mask & IN_CLOSE_NOWRITE) log_message(LOG_INFO, " IN_CLOSE_NOWRITE"); if (event->mask & IN_CREATE) log_message(LOG_INFO, " IN_CREATE"); if (event->mask & IN_DELETE) log_message(LOG_INFO, " IN_DELETE"); if (event->mask & IN_DELETE_SELF) log_message(LOG_INFO, " IN_DELETE_SELF"); if (event->mask & IN_MODIFY) log_message(LOG_INFO, " IN_MODIFY"); if (event->mask & IN_MOVE_SELF) log_message(LOG_INFO, " IN_MOVE_SELF"); if (event->mask & IN_MOVED_FROM) log_message(LOG_INFO, " IN_MOVED_FROM"); if (event->mask & IN_MOVED_TO) log_message(LOG_INFO, " IN_MOVED_TO"); if (event->mask & IN_OPEN) log_message(LOG_INFO, " IN_OPEN"); if (event->mask & IN_IGNORED) log_message(LOG_INFO, " IN_IGNORED"); if (event->mask & IN_ISDIR) log_message(LOG_INFO, " IN_ISDIR"); if (event->mask & IN_Q_OVERFLOW) log_message(LOG_INFO, " IN_Q_OVERFLOW"); if (event->mask & IN_UNMOUNT) log_message(LOG_INFO, " IN_UNMOUNT"); #endif if (file_wd != -1 && event->wd == file_wd) { #if 0 if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) { inotify_rm_watch(thread->u.f.fd, file_wd); #ifdef RELOAD_DEBUG log_message(LOG_INFO, "Removed watch %d", file_wd); #endif file_wd = -1; cancel_reload(true); } #endif if (event->mask & (IN_CLOSE_WRITE)) read_file(); } if (event->wd == dir_wd) { if (event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) { /* The directory has gone */ cancel_reload(true); close(thread->u.f.fd); log_message(LOG_INFO, "Directory of reload timer file has disappeared. Monitoring stopped."); return; } /* coverity[string_null] */ if (event->mask & (IN_CREATE | IN_MOVED_TO | IN_DELETE | IN_MOVED_FROM) && event->len && !strcmp(event->name, file_name)) { if (event->mask & (IN_CREATE | IN_MOVED_TO)) { file_wd = watch_file(thread->u.f.fd); #ifdef RELOAD_DEBUG log_message(LOG_INFO, "file_wd = %d", file_wd); #endif } else if (event->mask & (IN_DELETE | IN_MOVED_FROM)) { inotify_rm_watch(thread->u.f.fd, file_wd); #ifdef RELOAD_DEBUG log_message(LOG_INFO, "Removed watch by IN_%s %d", (event->mask & IN_DELETE) ? "DELETE" : "MOVED_FROM", file_wd); #endif file_wd = -1; cancel_reload(true); } } } } } inotify_thread = thread_add_read(master, inotify_event_thread, NULL, thread->u.f.fd, TIMER_NEVER, 0); } void start_reload_monitor(void) { int inotify_fd; char *dir; #ifdef RELOAD_DEBUG char time_buf[20]; #endif inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); file_name = strrchr(global_data->reload_time_file, '/'); if (!file_name) { dir = MALLOC(2); dir[0] = '/'; dir[1] = '\0'; } else { dir = MALLOC(file_name - global_data->reload_time_file + 1); strncpy(dir, global_data->reload_time_file, file_name - global_data->reload_time_file); } if ((dir_wd = inotify_add_watch(inotify_fd, dir, IN_CREATE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM | IN_DELETE_SELF | IN_MOVE_SELF)) == -1) { log_message(LOG_INFO, "Unable to monitor reload timer file directory %s- ignoring", dir); FREE(dir); return; } FREE(dir); if (!file_name) file_name = global_data->reload_time_file; else file_name++; file_wd = watch_file(inotify_fd); #ifdef RELOAD_DEBUG log_message(LOG_INFO, "dir_wd = %d, file_wd = %d", dir_wd, file_wd); if (global_data->reload_time) log_message(LOG_INFO, "Reload scheduled for %s", format_time_t(time_buf, sizeof(time_buf), global_data->reload_time)); #endif inotify_thread = thread_add_read(master, inotify_event_thread, NULL, inotify_fd, TIMER_NEVER, 0); } void stop_reload_monitor(void) { int fd; if (!inotify_thread) return; fd = inotify_thread->u.f.fd; thread_cancel(inotify_thread); inotify_thread = NULL; cancel_reload(false); close(fd); file_name = NULL; } #ifdef THREAD_DUMP void register_reload_addresses(void) { register_thread_address("inotify_event_thread", inotify_event_thread); register_thread_address("reload_timer_thread", reload_timer_thread); } #endif keepalived-2.3.3/snap/0000775000175000017500000000000014771067003010341 5keepalived-2.3.3/snap/snapcraft.yaml0000664000175000017500000005107014766544174013146 name: keepalived adopt-info: keepalived summary: High availability VRRP and load-balancing for Linux description: | Keepalived provides simple and robust loadbalancing and high-availability to Linux based infrastructures using VRRP and the well-known Linux Virtual Server (IPVS) kernel module. grade: stable confinement: classic base: core22 # Note: When change base to core24 or later, architectures needs to be changed to platforms architectures: - build-on: [amd64] - build-on: [arm64] - build-on: [armhf] - build-on: [ppc64el] - build-on: [s390x] - build-on: [riscv64] #platforms: # amd64: # arm64: # armhf: # ppc64el: # s390x: # riscv64: # linux-libc-dev for 5.08, 4.18 and 3.10 are no longer available via # http://security.ubuntu.com/ubuntu/pool/main/l/linux/ # or # http://ports.ubuntu.com/pool/main/l/linux/ # and will disappear for other versions is due course. # # To find the relevant files, go to launchpad.net/ubuntu/CODENAME/ARCH/linux-libc-dev # where CODENAME is, for example cosmic for Ubuntu 18.10, and ARCH is each of amd64, # ppc64el, arm64, armhf, s390x and riscv64. # Select the most recent version, and click on link under Version column (RHS). Under # Downloadable files is a link to the .deb file. That is what we need in DEB_URL. # # We should probably update the locations of the files to use this more difficult way # of finding them once the Ubuntu version that provides them goes EOL. apps: daemon: daemon: forking command: bin/keepalived-wrapper environment: LD_LIBRARY_PATH: "$SNAP/usr/lib/$SNAP_ARCH-linux-gnu" keepalived: command: bin/keepalived-wrapper "519": # First post Linux 5.18 version - Ubuntu 22.10 command: usr/sbin/keepalived-519 "508": # Fedora 33 and Ubuntu 20.10 command: usr/sbin/keepalived-508 "504": # Ubuntu 20.04 command: usr/sbin/keepalived-504 "418": # RHEL 8 command: usr/sbin/keepalived-418 "415": # Ubuntu 18.04 command: usr/sbin/keepalived-415 "313": # RHEL 7 - kernel 3.10 but launchpad no longer has 3.10 packages command: usr/sbin/keepalived-313 genhash: command: usr/bin/genhash # This first part will build with the kernel headers for the Ubuntu version specified in base # - e.g. Ubuntu 22.04 (Jammy) has kernel 5.15. parts: keepalived: plugin: autotools source: . source-type: git autotools-configure-parameters: - --prefix=/usr - --with-samples-dir='$(docdir)' - --enable-bfd - --enable-dbus - --enable-json - --enable-regex - --enable-snmp - --enable-snmp-rfc - --disable-libipset-dynamic - --disable-systemd override-build: | craftctl default VER= if [ -f lib/git-commit.h ]; then COMMIT=`grep GIT_COMMIT lib/git-commit.h | wc -l || true` if [ $COMMIT -eq 1 ]; then VER=$(grep GIT_COMMIT lib/git-commit.h | cut -d'"' -f2) fi fi if [ -z $VER ]; then VER=`grep "^AC_INIT" configure.ac | tr '[' ']' | cut -d']' -f4` fi craftctl set version=$VER echo -n "Build kernel:" printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE /usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") KERNEL_VER=$(printf "%d%2.2d" $(printf "%6.6x" $(grep LINUX_VERSION_CODE /usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)../0x\1 0x\2/")) rm ${CRAFT_PART_INSTALL}/usr/bin/genhash ln -s ../sbin/keepalived-$KERNEL_VER ${CRAFT_PART_INSTALL}/usr/bin/genhash build-packages: - libxtables-dev - libip4tc-dev - libip6tc-dev - libipset-dev - libglib2.0-dev - libmagic-dev - libmnl-dev - libnftnl-dev - libnl-3-dev - libnl-genl-3-dev - libnfnetlink-dev - libpcre2-dev - libsnmp-dev - libssl-dev - libkmod-dev stage-packages: - libnfnetlink0 - libipset13 - libglib2.0-0 - libmagic1 - libmnl0 - libnftnl11 - libnl-3-200 - libnl-genl-3-200 - libpcre2-8-0 - libsnmp40 organize: 'usr/sbin/keepalived': usr/sbin/keepalived-515 linux-headers-519: plugin: nil build-packages: - dpkg - dpkg-dev - wget after: - keepalived override-pull: | KERNEL_VER="5.19.0" ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) # Packages from Ubuntu 22.10 (Kinetic) if [ $ARCH = amd64 ]; then DEB_URL="http://launchpadlibrarian.net/672569528/linux-libc-dev_5.19.0-46.47_amd64.deb" elif [ $ARCH = ppc64el ]; then DEB_URL="http://launchpadlibrarian.net/672570374/linux-libc-dev_5.19.0-46.47_ppc64el.deb" elif [ $ARCH = arm64 ]; then DEB_URL="http://launchpadlibrarian.net/672667063/linux-libc-dev_5.19.0-46.47_arm64.deb" elif [ $ARCH = armhf ]; then DEB_URL="http://launchpadlibrarian.net/672578326/linux-libc-dev_5.19.0-46.47_armhf.deb" elif [ $ARCH = s390x ]; then DEB_URL="http://launchpadlibrarian.net/672563386/linux-libc-dev_5.19.0-46.47_s390x.deb" elif [ $ARCH = riscv64 ]; then DEB_URL="http://launchpadlibrarian.net/672563745/linux-libc-dev_5.19.0-46.47_riscv64.deb" fi wget --quiet ${DEB_URL} -O ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb override-build: | craftctl default KERNEL_VER="5.19.0" echo -n "Unpacking kernel $KERNEL_VER headers..." dpkg -x ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb ${CRAFT_STAGE}/keepalived-linux-libc-dev printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") rm -f ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb # Move header from the part to the host ARCH_DIR=$(ls -d ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/*/asm | sed -e "s:/asm$::" -e "s:.*/::") for d in linux asm-generic $ARCH_DIR/asm; do dest=/usr/include/$d rm -rf $dest mv ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/$d ${dest%/*} done rm -rf ${CRAFT_STAGE}/keepalived-linux-libc-dev echo -n "Build kernel:" printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE /usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") stage: - -* prime: - -* keepalived-519: plugin: autotools source: . source-type: git after: - linux-headers-519 autotools-configure-parameters: - --prefix=/usr - --with-samples-dir='$(docdir)' - --enable-bfd - --enable-dbus - --enable-json - --enable-regex - --enable-snmp - --enable-snmp-rfc - --disable-libipset-dynamic organize: 'usr/sbin/keepalived': usr/sbin/keepalived-519 'usr/etc/dbus-1/system.d/org.keepalived.Vrrp1.conf': etc/dbus-1/system.d/org.keepalived.Vrrp1.conf stage: - usr/sbin/keepalived-519 - etc/dbus-1/system.d/org.keepalived.Vrrp1.conf - usr/share/dbus-1/interfaces/org.keepalived.Vrrp1.Instance.xml - usr/share/dbus-1/interfaces/org.keepalived.Vrrp1.Vrrp.xml prime: - usr/sbin/keepalived-519 - etc/dbus-1/system.d/org.keepalived.Vrrp1.conf - usr/share/dbus-1/interfaces/org.keepalived.Vrrp1.Instance.xml - usr/share/dbus-1/interfaces/org.keepalived.Vrrp1.Vrrp.xml linux-headers-508: plugin: nil build-packages: - dpkg - dpkg-dev - wget after: - keepalived-519 override-pull: | KERNEL_VER="5.8.0" ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) # Packages from 20.10 (Groovy) if [ $ARCH = amd64 ]; then DEB_URL="http://launchpadlibrarian.net/548300974/linux-libc-dev_5.8.0-63.71_amd64.deb" elif [ $ARCH = ppc64el ]; then DEB_URL="http://launchpadlibrarian.net/548289994/linux-libc-dev_5.8.0-63.71_ppc64el.deb" elif [ $ARCH = arm64 ]; then DEB_URL="http://launchpadlibrarian.net/548341043/linux-libc-dev_5.8.0-63.71_arm64.deb" elif [ $ARCH = armhf ]; then DEB_URL="http://launchpadlibrarian.net/548325871/linux-libc-dev_5.8.0-63.71_armhf.deb" elif [ $ARCH = s390x ]; then DEB_URL="http://launchpadlibrarian.net/548279675/linux-libc-dev_5.8.0-63.71_s390x.deb" elif [ $ARCH = riscv64 ]; then DEB_URL="http://launchpadlibrarian.net/548280908/linux-libc-dev_5.8.0-63.71_riscv64.deb" fi wget --quiet ${DEB_URL} -O ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb override-build: | craftctl default KERNEL_VER="5.8.0" echo -n "Unpacking kernel $KERNEL_VER headers..." dpkg -x ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb ${CRAFT_STAGE}/keepalived-linux-libc-dev printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") rm -f ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb # Move header from the part to the host ARCH_DIR=$(ls -d ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/*/asm | sed -e "s:/asm$::" -e "s:.*/::") for d in linux asm-generic $ARCH_DIR/asm; do dest=/usr/include/$d rm -rf $dest mv ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/$d ${dest%/*} done rm -rf ${CRAFT_STAGE}/keepalived-linux-libc-dev echo -n "Build kernel:" printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE /usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") stage: - -* prime: - -* keepalived-508: plugin: autotools source: . source-type: git after: - linux-headers-508 autotools-configure-parameters: - --prefix=/usr - --with-samples-dir='$(docdir)' - --enable-bfd - --enable-dbus - --enable-json - --enable-regex - --enable-snmp - --enable-snmp-rfc - --disable-libipset-dynamic organize: 'usr/sbin/keepalived': usr/sbin/keepalived-508 stage: - usr/sbin/keepalived-508 prime: - usr/sbin/keepalived-508 linux-headers-504: plugin: nil build-packages: - dpkg - dpkg-dev - wget after: - keepalived-508 override-pull: | KERNEL_VER="5.4.0" ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) if [ $ARCH = amd64 ]; then ARCHIVE_URL="http://security.ubuntu.com/ubuntu/pool/main/l/linux/" else ARCHIVE_URL="http://ports.ubuntu.com/pool/main/l/linux/" fi DEB_SUFFIX=$(wget ${ARCHIVE_URL} -O - | grep linux-libc-dev | cut -d'_' -f2-3 | cut -d'"' -f1 | grep ${ARCH} | grep ${KERNEL_VER} | tail -n1) wget --quiet ${ARCHIVE_URL}/linux-libc-dev_${DEB_SUFFIX} -O ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb override-build: | craftctl default KERNEL_VER="5.4.0" echo -n "Unpacking kernel $KERNEL_VER headers..." dpkg -x ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb ${CRAFT_STAGE}/keepalived-linux-libc-dev printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") rm -f ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb # Move header from the part to the host ARCH_DIR=$(ls -d ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/*/asm | sed -e "s:/asm$::" -e "s:.*/::") for d in linux asm-generic $ARCH_DIR/asm; do dest=/usr/include/$d rm -rf $dest mv ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/$d ${dest%/*} done rm -rf ${CRAFT_STAGE}/keepalived-linux-libc-dev echo -n "Build kernel:" printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE /usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") stage: - -* prime: - -* keepalived-504: plugin: autotools source: . source-type: git after: - linux-headers-504 autotools-configure-parameters: - --prefix=/usr - --with-samples-dir='$(docdir)' - --enable-bfd - --enable-dbus - --enable-json - --enable-regex - --enable-snmp - --enable-snmp-rfc - --disable-libipset-dynamic organize: 'usr/sbin/keepalived': usr/sbin/keepalived-504 stage: - usr/sbin/keepalived-504 prime: - usr/sbin/keepalived-504 linux-headers-418: plugin: nil build-packages: - dpkg - dpkg-dev - wget after: - keepalived-504 override-pull: | KERNEL_VER="4.18.0" ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) # From Ubuntu 18.10 (Cosmic) if [ $ARCH = amd64 ]; then DEB_URL="http://launchpadlibrarian.net/431512728/linux-libc-dev_4.18.0-26.27_amd64.deb" elif [ $ARCH = ppc64el ]; then DEB_URL="http://launchpadlibrarian.net/431492858/linux-libc-dev_4.18.0-26.27_ppc64el.deb" elif [ $ARCH = arm64 ]; then DEB_URL="http://launchpadlibrarian.net/431531681/linux-libc-dev_4.18.0-26.27_arm64.deb" elif [ $ARCH = armhf ]; then DEB_URL="http://launchpadlibrarian.net/431525966/linux-libc-dev_4.18.0-26.27_armhf.deb" elif [ $ARCH = s390x ]; then DEB_URL="http://launchpadlibrarian.net/431485536/linux-libc-dev_4.18.0-26.27_s390x.deb" fi # Ubuntu 18.10 Cosmic Cuttle had no riscv64 support if [ $ARCH != riscv64 ]; then wget --quiet ${DEB_URL} -O ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb fi override-build: | craftctl default KERNEL_VER="4.18.0" ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) if [ $ARCH != riscv64 ]; then echo -n "Unpacking kernel $KERNEL_VER headers..." dpkg -x ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb ${CRAFT_STAGE}/keepalived-linux-libc-dev printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") rm -f ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb # Move header from the part to the host ARCH_DIR=$(ls -d ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/*/asm | sed -e "s:/asm$::" -e "s:.*/::") for d in linux asm-generic $ARCH_DIR/asm; do dest=/usr/include/$d rm -rf $dest mv ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/$d ${dest%/*} done fi rm -rf ${CRAFT_STAGE}/keepalived-linux-libc-dev echo -n "Build kernel:" printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE /usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") stage: - -* prime: - -* keepalived-418: plugin: autotools source: . source-type: git after: - linux-headers-418 autotools-configure-parameters: - --prefix=/usr - --with-samples-dir='$(docdir)' - --enable-bfd - --enable-dbus - --enable-json - --enable-regex - --enable-snmp - --enable-snmp-rfc - --disable-libipset-dynamic organize: 'usr/sbin/keepalived': usr/sbin/keepalived-418 stage: - usr/sbin/keepalived-418 prime: - usr/sbin/keepalived-418 linux-headers-415: plugin: nil build-packages: - dpkg - dpkg-dev - wget after: - keepalived-418 override-pull: | KERNEL_VER="4.15.0" ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) if [ $ARCH = amd64 ]; then ARCHIVE_URL="http://security.ubuntu.com/ubuntu/pool/main/l/linux/" elif [ $ARCH != riscv64 ]; then ARCHIVE_URL="http://ports.ubuntu.com/pool/main/l/linux/" fi # Ubuntu 18.40 (Bionic Beaver) had no riscv64 support if [ $ARCH != riscv64 ]; then DEB_SUFFIX=$(wget ${ARCHIVE_URL} -O - | grep linux-libc-dev | cut -d'_' -f2-3 | cut -d'"' -f1 | grep ${ARCH} | grep ${KERNEL_VER} | tail -n1) wget --quiet ${ARCHIVE_URL}/linux-libc-dev_${DEB_SUFFIX} -O ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb fi override-build: | craftctl default KERNEL_VER="4.15.0" # Move header from the part to the host ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) if [ $ARCH != riscv64 ]; then echo -n "Unpacking kernel $KERNEL_VER headers..." dpkg -x ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb ${CRAFT_STAGE}/keepalived-linux-libc-dev printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") rm -f ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb ARCH_DIR=$(ls -d ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/*/asm | sed -e "s:/asm$::" -e "s:.*/::") for d in linux asm-generic $ARCH_DIR/asm; do dest=/usr/include/$d rm -rf $dest mv ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/$d ${dest%/*} done fi rm -rf ${CRAFT_STAGE}/keepalived-linux-libc-dev echo -n "Build kernel:" printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE /usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") stage: - -* prime: - -* keepalived-415: plugin: autotools source: . source-type: git after: - linux-headers-415 autotools-configure-parameters: - --prefix=/usr - --with-samples-dir='$(docdir)' - --enable-bfd - --enable-dbus - --enable-json - --enable-regex - --enable-snmp - --enable-snmp-rfc - --disable-libipset-dynamic organize: 'usr/sbin/keepalived': usr/sbin/keepalived-415 stage: - usr/sbin/keepalived-415 prime: - usr/sbin/keepalived-415 linux-headers-313: plugin: nil build-packages: - dpkg - dpkg-dev - wget after: - keepalived-415 override-pull: | KERNEL_VER="3.13.0" ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) if [ $ARCH = amd64 ] || [ $ARCH = ppc64el ] || [ $ARCH = arm64 ]; then DEB_URL="http://launchpadlibrarian.net/422971061/linux-libc-dev_3.13.0-170.220_amd64.deb" elif [ $ARCH = armhf ]; then DEB_URL="http://launchpadlibrarian.net/422971427/linux-libc-dev_3.13.0-170.220_i386.deb" fi if [ $ARCH != s390x ] && [ $ARCH != riscv64 ]; then # riscv64 and s390x were not supported on Trusty, so there are no kernel headers for them. # Just let them build with the previous headers. wget --quiet ${DEB_URL} -O ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb fi override-build: | craftctl default KERNEL_VER="3.13.0" # Move header from the part to the host ARCH=$(dpkg-architecture -q DEB_BUILD_ARCH) if [ $ARCH != s390x ] && [ $ARCH != riscv64 ]; then echo -n "Unpacking kernel $KERNEL_VER headers..." dpkg -x ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb ${CRAFT_STAGE}/keepalived-linux-libc-dev printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") rm -f ${CRAFT_STAGE}/linux-libc-dev-$KERNEL_VER.deb # Move header from the part to the host ARCH_DIR=$(ls -d ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/*/asm | sed -e "s:/asm$::" -e "s:.*/::") for d in linux asm-generic $ARCH_DIR/asm; do dest=/usr/include/$d rm -rf $dest mv ${CRAFT_STAGE}/keepalived-linux-libc-dev/usr/include/$d ${dest%/*} done fi rm -rf ${CRAFT_STAGE}/keepalived-linux-libc-dev echo -n "Build kernel:" printf " %d.%d.%d\n" $(printf "%6.6x" $(grep LINUX_VERSION_CODE /usr/include/linux/version.h | sed -e "s/.*CODE //") | sed -e "s/\(..\)\(..\)/0x\1 0x\2 0x/") stage: - -* prime: - -* keepalived-313: plugin: autotools source: . source-type: git after: - linux-headers-313 autotools-configure-parameters: - --prefix=/usr - --with-samples-dir='$(docdir)' - --enable-bfd - --enable-dbus - --enable-json - --enable-regex - --enable-snmp - --enable-snmp-rfc - --disable-libipset-dynamic organize: 'usr/sbin/keepalived': usr/sbin/keepalived-313 stage: - usr/sbin/keepalived-313 prime: - usr/sbin/keepalived-313 keepalived-wrapper: plugin: dump source: snap-tools organize: 'keepalived-wrapper': bin/ keepalived-2.3.3/snap/hooks/0000775000175000017500000000000014105735173011465 5keepalived-2.3.3/snap/hooks/post-refresh0000775000175000017500000000100313371016233013737 #!/bin/sh # Create a default (empty) configuration file if [ ! -f /etc/keepalived/keepalived.conf ]; then mkdir -p /etc/keepalived touch /etc/keepalived/keepalived.conf fi # Copy the DBus files to the required directories for file in /etc/dbus-1/system.d/org.keepalived.Vrrp1.conf /usr/share/dbus-1/interfaces/org.keepalived.Vrrp1.Instance.xml /usr/share/dbus-1/interfaces/org.keepalived.Vrrp1.Vrrp.xml; do DIR=`echo $file | sed -e "s:/[^/]*$::"` if [ -d $DIR ]; then cp -p $SNAP$file $file fi done keepalived-2.3.3/snap/hooks/install0000775000175000017500000000000013371016233022241 1keepalived-2.3.3/snap/hooks/post-refreshkeepalived-2.3.3/TODO0000664000175000017500000001536214023433056010012 BUG - If have VIP on a different interface from the VRRP instance, the interface isn't tracked and so we do nothing if the interface is deleted. Add INTERFACE_CHECK and ADDRESS_CHECK checkers - see issue#1679 and pull request #1680. Fix building on CentOS 6.10 Add a TCP_KEEPALIVE_CHECK that established a TCP connection and uses keepalives like hg635_config to check the remote end is there. Remove definitions that simply access structure fields, e.g. VRRP_ISUP Stop making structure fields such as base_ifp and configured_ifp conditional. In parsers, using LIST_TAIL to find object to configure doesn't work if error in first line means not created. TAIL could be null, or previous object. No SMTP alert for vrrp instance FAULT/BACKUP/MASTER following track file changes Shutdown SMTP messages not completing before keepalived terminates (especially for connect timeout. Causes memory leak!) Test LVS forwarding via VIP if no_accept set If interface is deleted and recreated, then index changes - does that cause OIDs to change? Is the RFC flawed? How do we deal with it? We could have our own ifindex incremented as we create interface_t's Add track_route, track_address PKG_CONFIG_* autoconf options - see man pkg.m4 Whatever you want ! Real servers Add quorum_weight, defaults to weight. May want to set different between quorum and IPVS weight Rationalise use of timer_now()/gettimeofday()/set_time_now() virtual route: 1. How do we handle virtual_routes { 192.168.210.0/24 via 10.1.0.1 } if there is no route to 10.1.0.1? If we go to fault state if it cannot be installed when attempt to transition to master, how do we know to transiton out of fault state. IPVS 1. Can we have an IPv4 fwmark and specify tunnel to IPv6, and vice versa etc. What does ipvsadm do? 2. Sort out IP_VS_SO vs LVS_CMD... 3. Make sure log/email messages contain relevant info re VS -> RS. See FMT_CHK The following are still outstanding from the ideas for what became v2.0.0: * Sort out termination in vrrp and checker - stop_vrrp/check, and phase2 are inconsistent between vrrp and checker. What about bfd? * A route with multiple nexthops will only be deleted if all interfaces are down. Each nexthop needs to record the interface, and only remove once all down * Don't add RTNMGRP_IPV4_ADDR/IPV6_ADDR if not monitoring one of those families * May want to stop VS down at start for alpha mode RSs (except SNMP) * Have a socket to connect to for receiving notifications. A process can register for what notifications it wants to receive. * Allow variable parameters to be passed to scripts - see issue #837 * Make tarball include git version if not a tag * If an address owner recovers from fault, transition directly to master * If configuration_state=MASTER && !OWNER, transition to master after 1 * advert_int + skew. Sort out all initialisation around states * Split vrrp_snmp.c into vrrp_snmp_keepalived.c vrrp_snmp_rfcv2.c and vrrp_snmp_rfcv3.c * Only send correct type of trap, and respond to correct SNMP version, controlled by config, defaults to type of instance. Flags snmp_v2 and snmp_v3 to force other, or both. * Ensure unicast peers groups check source address of received advert so that the same VRID can be used between different peer groups on the same interface. * Add process checking: Add track_process for vrrp instances Add PROCESS_CHECK for checkers - Find PID and remember it. If PID has gone, try and find new pid Look at how pidof/killall find processes * Allow dynamic definitions, e.g. $_VI_NAME Also $*_INSTANCE net_namspace $_INSTANCE so $* means only do if $_INSTANCE not blank. */ * ng-scheduler Other issues awaiting resolution: ipvs_group_range_cmd() appears nonsense, and inet_stor returning a uint8_t doesn't work for IPv6. The virtual_service_entry_t thing needs range to be uint32_t. Why would mask be 0xffffffff for IPv6? ip_vs_daemon_kern vs /usr/include/linux/ip_vs.h ip_vs_daemon_user vrrp_timer_fd should return 0 if an fd's timer is in the past? Make vrrp->send_buffer a single buffer for all Check timers passto to thread_... functions are reasonable, and stacktrace if not. In function socket_state, should thread_add_write use timer_long() or -timer_long() vrrp_timer_fd() - see comment In thread functions with a timer, ensure not > LONG_MAX (or even some lower value); Change tcp_socket_state etc to socket_state etc Add noreturn function attribute on stop_vrrp/check in ipvswrapper.c, get rid of static srule etc and make them procedure local and pass as parameters Stop passing base_ifp to netlink3_set_interface_parameters() etc ipsecah issues ============== 1. sync and vmac ignore counter 2. Start up just after master dies, but have lowest priority so become master with counter == 1 3. cycle won't happen 4. In vrrp_backup, don't check auth type matches 5. ? if cycle becomes set, we must become backup. How do we get out of state? epoll ===== Use timerfd (see timerfd_create(2)) for microsecond timing with epoll, and do not bother with its timeout. If not available, then simply use the epoll timer. Optimise calls to timer_now() and see set_time_now() ==================================================== After select completes, get time. Before calculating next select expire time, get time again. To test time processing, save time after select and log time taken before next select. Also initialise timer at startup. Add api ======= Add pipe for updates ==================== track_script like for absolute or relative priority Upstream issues =============== 1. Kernel. Socket receive buffers growing to fill memory See issue #839. If the following setting are in place: net.core.rmem_default = 37748736 net.core.rmem_max = 37748736 and vrrp_tx_bufs_policy NO_SEND_RX is configured, then all system memory can be consumed. Why is it not limited to 37748736 bytes? 2. Kernel. Corruption when netlink sends status of large number of links - see issues #392/#803. When using default socket receive buffer size, and have 500 vmac interfaces configured on a physical interface and the physical interface is downed, lots of netlink messages are received, and we get an ENOBUFS. However, after that we then see a repeat of some of the earlier messages, so it looks like a circular buffer corruption. Running ip -ts monitor link addr route we see precisely the same problem at exactly the same message, although which message it is varies each time. 3. net-snmp. Display-hint only works on indices. See issue #866. # snmpwalk -v2c -c public localhost KEEPALIVED-MIB::virtualServerAddress KEEPALIVED-MIB::virtualServerAddress.1 = STRING: " 0," The address is attempted to be output as text, rather than using the display hint keepalived-2.3.3/tools/0000775000175000017500000000000014772274311010543 5keepalived-2.3.3/tools/timed_reload0000775000175000017500000000512213611065644013037 #!/bin/bash # This script will cause keepalived to reload its configuration # at a specified time. KILL=kill TIME= INSTANCE= ALL=0 export HOUR show_help() { cat </dev/null | wc -l) [[ ! -f /run/keepalived.pid && $NUM_PIDS -eq 0 ]] && echo Cannot find any pid file && exit 1 fi set $(<<<$TIME tr ":" " ") if [[ $# -lt 2 || $# -gt 3 ]]; then echo $0 Time format is HH:MM or HH:MM:SS exit 1 fi strip0 HOUR $1 strip0 MIN $2 [[ $# -eq 3 ]] && strip0 SEC $3 set $(date +"%H %M %S %N") strip0 C_HOUR $1 strip0 C_MIN $2 strip0 C_SEC $3 strip0 C_NSEC $4 # echo $HOUR - $C_HOUR \* 3600 + $MIN - $C_MIN \* 60 + $SEC - $C_SEC DELAY=$(( (HOUR - C_HOUR) * 3600 + (MIN - C_MIN) * 60 + SEC - C_SEC )) if [[ $C_NSEC -ne 0 ]]; then : $((DELAY--)) NSEC_DELAY=$((1000000000 - $C_NSEC)) NSEC_DELAY=$( <<<$NSEC_DELAY sed -e "s/^0*//") NSEC_DELAY=.$(printf "%9.9d" $NSEC_DELAY) else NSEC_DELAY= fi if [[ $DELAY -lt 0 ]]; then echo Time specified is in the past exit 1 fi sleep $DELAY$NSEC_DELAY echo Reloading at $(date +"%H:%M:%S.%N") if [[ $ALL -eq 1 ]]; then for f in /run/keepalived.pid $(find /run/keepalived -name keepalived.pid); do [[ -f $f ]] && $KILL -HUP $(cat $f) done elif [[ -n $INSTANCE ]]; then $KILL -HUP $(cat /run/keepalived/$INSTANCE/keepalived.pid) else $KILL -HUP $(cat /run/keepalived.pid) fi keepalived-2.3.3/build_setup0000775000175000017500000000014413654223247011567 #!/bin/sh mkdir -p build-aux aclocal --install -I m4 autoheader automake --add-missing autoreconf keepalived-2.3.3/configure.ac0000664000175000017500000040307714772274000011617 # # Keepalived OpenSource project. # # Configuration template file for keepalived. # autoconf will generate & check deps for proper compilation # # Copyright (C) 2001-2018 Alexandre Cassen, AC_DEFUN([add_to_var], [$1="$$1 $2"]) AC_DEFUN([add_to_var_ind], [eval $1=\"\$$1 $2\"]) dnl " AC_DEFUN([add_to_var_ind_unique], ADD_NEW= [ eval var=\$$1 for item in $2; do echo " $var " | $GREP -q " $item " if test $? -ne 0; then add_to_var([ADD_NEW], [$item]) fi done add_to_var_ind([$1], [$ADD_NEW]) ]) AC_DEFUN([add_pkg_config], [ if test -n "$2"; then KA_PKG_PFX=$2 else KA_PKG_PFX=KA fi add_to_var_ind_unique([${KA_PKG_PFX}_CPPFLAGS], [`$PKG_CONFIG --cflags-only-I $1`]) add_to_var_ind_unique([${KA_PKG_PFX}_CFLAGS], [`$PKG_CONFIG --cflags-only-other $1`]) if test .$3 = .remove-requires; then REQUIRES=`$PKG_CONFIG --print-requires $1` var=`$PKG_CONFIG --libs-only-l $1` for r in $REQUIRES; do REQ_LIBS=`$PKG_CONFIG --libs-only-l $r` for l in $REQ_LIBS; do var=`echo " $var " | sed -e "s: $l : :g"` done done var=`echo $var | sed -e "s:^ *::" -e "s: *$::"` eval ${KA_PKG_PFX}_LIBS="\"$var\"" var=`echo $var | sed -e "s/-l//g"` eval ${KA_PKG_PFX}_LIB_NAMES="\"$var\"" else add_to_var_ind_unique([${KA_PKG_PFX}_LIBS], [`$PKG_CONFIG --libs $1`]) fi ]) AC_DEFUN([add_pkg_config_without_libs], [ if test -n "$2"; then KA_PKG_PFX=$2 else KA_PKG_PFX=KA fi add_to_var_ind_unique([${KA_PKG_PFX}_CPPFLAGS], [`$PKG_CONFIG --cflags-only-I $1`]) add_to_var_ind_unique([${KA_PKG_PFX}_CFLAGS], [`$PKG_CONFIG --cflags-only-other $1`]) ]) AC_DEFUN([add_config_opt], [add_to_var([CONFIG_OPTIONS], [$1])]) AC_DEFUN([add_system_opt], [add_to_var([SYSTEM_OPTIONS], [$1])]) AC_DEFUN([get_lib_name], [ if test $LDD = :; then AC_MSG_ERROR([ldd is required for dynamic run-time linking support]) fi SAV_LIBS="$LIBS" LIBS="$LIBS -l$1" AC_LINK_IFELSE([AC_LANG_SOURCE([[ extern void $2(void); int main(void) { $2(); return 0; } ]])], [ LIB_DETAILS=`$LDD ./conftest$EXEEXT | grep $1.so | sed -e "s/^[[ \t]]*//"` LIB_NAME=`echo $LIB_DETAILS | sed -e "s/ .*//"` ],[ ]) LIBS="$SAV_LIBS" ]) # AS_VAR_COPY was introduced in autoconf 2.63b. # Remove the following definition once require autoconf >= 2.64. m4_ifndef([AS_VAR_COPY], [m4_define([AS_VAR_COPY], [AS_LITERAL_IF([$1[]$2], [$1=$$2], [eval $1=\$$2])])]) dnl ----[ Process this file with autoconf to produce a configure script ]---- AC_PREREQ([2.63]) AC_INIT([Keepalived], [2.3.3], [keepalived-users@groups.io], [], [http://www.keepalived.org/]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AC_LANG([C]) AM_INIT_AUTOMAKE([-Wall -Werror -Woverride foreign]) AC_CONFIG_SRCDIR([keepalived/core/main.c]) AC_CONFIG_HEADERS([lib/config.h lib/config_warnings.h]) AH_TOP( [ #ifndef _CONFIG_H #define _CONFIG_H ]) AH_BOTTOM( [ #include "config_warnings.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #endif]) AC_CONFIG_FILES([Makefile keepalived/Makefile lib/Makefile keepalived/core/Makefile keepalived.spec \ Dockerfile \ keepalived/check/Makefile keepalived/vrrp/Makefile \ keepalived/bfd/Makefile doc/Makefile bin_install/Makefile keepalived/dbus/Makefile \ keepalived/etc/Makefile keepalived/etc/init/Makefile keepalived/etc/init.d/Makefile \ keepalived/etc/sysconfig/Makefile keepalived/etc/keepalived/Makefile \ keepalived/trackers/Makefile \ doc/man/man8/Makefile doc/man/man5/Makefile doc/man/man1/Makefile]) MAINTAINERCLEANFILES="*~ *.orig *.rej core core.*" AC_SUBST(MAINTAINERCLEANFILES) CONFIG_OPTIONS= SYSTEM_OPTIONS= AM_SILENT_RULES([yes]) PKG_PROG_PKG_CONFIG dnl ----[ Keepalived specific configure options ]---- AC_ARG_ENABLE(lvs-syncd, [AS_HELP_STRING([--disable-lvs-syncd], [do not use LVS synchronization daemon])]) AC_ARG_ENABLE(lvs, [AS_HELP_STRING([--disable-lvs], [do not use the LVS framework])]) AC_ARG_ENABLE(lvs-64bit-stats, [AS_HELP_STRING([--disable-lvs-64bit-stats], [do not use the LVS 64-bit stats])]) AC_ARG_ENABLE(vrrp, [AS_HELP_STRING([--disable-vrrp], [do not use the VRRP framework])]) AC_ARG_ENABLE(bfd, [AS_HELP_STRING([--enable-bfd], [use the BFD framework])]) AC_ARG_WITH(kernel-dir, [AS_HELP_STRING([--with-kernel-dir=DIR], [path to linux kernel source directory])], [AS_HELP_STRING([kernel_src_path="$withval"],], [[kernel_src_path=""])]) AC_ARG_WITH(samples-dir, [AS_HELP_STRING([--with-samples-dir=DIR], [specify location to install sample config files [SYSCONFDIR/samples]])]) AC_ARG_ENABLE(fwmark, [AS_HELP_STRING([--disable-fwmark], [compile without SO_MARK support])]) AC_ARG_ENABLE(snmp, [AS_HELP_STRING([--enable-snmp], [compile with SNMP support])]) AC_ARG_ENABLE(snmp-vrrp, [AS_HELP_STRING([--enable-snmp-vrrp], [compile with SNMP vrrp support])]) AC_ARG_ENABLE(snmp-keepalived, [AS_HELP_STRING([--enable-snmp-keepalived], [obsolete - use --enable-snmp-vrrp])]) AC_ARG_ENABLE(snmp-checker, [AS_HELP_STRING([--enable-snmp-checker], [compile with SNMP checker support])]) AC_ARG_ENABLE(snmp-rfc, [AS_HELP_STRING([--enable-snmp-rfc], [compile with SNMP RFC2787 (VRRPv2) and SNMP RFC6527 (VRRPv3) support])]) AC_ARG_ENABLE(snmp-rfcv2, [AS_HELP_STRING([--enable-snmp-rfcv2], [compile with SNMP RFC2787 (VRRPv2) support])]) AC_ARG_ENABLE(snmp-rfcv3, [AS_HELP_STRING([--enable-snmp-rfcv3], [compile with SNMP RFC6527 (VRRPv3) support])]) AC_ARG_ENABLE(snmp-reply-v3-for-v2, [AS_HELP_STRING([--disable-snmp-reply-v3-for-v2], [disable RFC6527 responses for VRRPv2 instances])]) AC_ARG_ENABLE(dbus, [AS_HELP_STRING([--enable-dbus], [compile with dbus support])]) AC_ARG_ENABLE(dbus-create-instance, [AS_HELP_STRING([--enable-dbus-create-instance], [compile with dbus support for creating instances])]) AC_ARG_ENABLE(regex, [AS_HELP_STRING([--enable-regex], [build with HTTP_GET regex checking])]) AC_ARG_ENABLE(vmac, [AS_HELP_STRING([--disable-vmac], [build without VMAC support])]) AC_ARG_ENABLE(nm, [AS_HELP_STRING([--enable-nm], [enable setting VMACs unmanaged by NetworkManager (for pre 1.18 NM)])]) AC_ARG_ENABLE(regex-timers, [AS_HELP_STRING([--enable-regex-timers], [build with HTTP_GET regex timers])]) AC_ARG_ENABLE(json, [AS_HELP_STRING([--enable-json], [compile with signal to dump configuration and stats as json])]) AC_ARG_ENABLE(clang, [AS_HELP_STRING([--enable-clang], [use clang compiler])]) AC_ARG_ENABLE(lto, [AS_HELP_STRING([--enable-lto], [use Link Time Optimisation])]) AC_ARG_ENABLE(reproducible-build, [AS_HELP_STRING([--enable-reproducible-build], [make builds reproducible])]) AC_ARG_WITH(init, [AS_HELP_STRING([--with-init=(upstart|systemd|SYSV|SUSE|openrc)], [specify init type])], [init_type="$withval"], [init_type=""]) AC_ARG_ENABLE(vrrp-auth, [AS_HELP_STRING([--disable-vrrp-auth], [compile without VRRP authentication])]) AC_ARG_ENABLE(checksum_compat, [AS_HELP_STRING([--disable-checksum-compat], [compile without v1.3.6 and earlier VRRPv3 unicast checksum compatibility])]) AC_ARG_ENABLE(routes, [AS_HELP_STRING([--disable-routes], [compile without ip rules/routes])]) AC_ARG_ENABLE(linkbeat, [AS_HELP_STRING([--disable-linkbeat], [build without linkbeat support])]) AC_ARG_ENABLE(sockaddr_storage, [AS_HELP_STRING([--enable-sockaddr-storage], [build using sockaddr_storage rather than smaller sockaddr for IPv4/6 only])]) AC_ARG_ENABLE(gnu-std-paths, [AS_HELP_STRING([--enable-gnu-std-paths], [use GNU standard paths for pid files etc])]) AC_ARG_ENABLE(dynamic-linking, [AS_HELP_STRING([--enable-dynamic-linking], [compile with/without dynamically linked libiptc/libipset/libnl])]) AC_ARG_ENABLE(iptables, [AS_HELP_STRING([--disable-iptables], [compile without iptables support])], [], [IPTABLES_SILENT=Yes]) AC_ARG_ENABLE(libiptc-dynamic, [AS_HELP_STRING([--enable-libiptc-dynamic], [compile with libiptc dynamically linked])]) AC_ARG_ENABLE(libipset-dynamic, [AS_HELP_STRING([--disable-libipset-dynamic], [compile with libipset statically linked])]) AC_ARG_ENABLE(libnl-dynamic, [AS_HELP_STRING([--enable-libnl-dynamic], [compile with libnl dynamically linked])]) AC_ARG_ENABLE(libipset, [AS_HELP_STRING([--disable-libipset], [compile without libipset])]) AC_ARG_ENABLE(nftables, [AS_HELP_STRING([--disable-nftables], [build without nftables support])], [], [NFTABLES_SILENT=Yes]) AC_ARG_ENABLE(libnl, [AS_HELP_STRING([--disable-libnl], [compile without libnl])]) AC_ARG_ENABLE(track-process, [AS_HELP_STRING([--disable-track-process], [build without track-process functionality])]) AC_ARG_ENABLE(systemd, [AS_HELP_STRING([--disable-systemd], [build without systemd integration])]) AC_ARG_WITH(run-dir, [AS_HELP_STRING([--with-run-dir=PATH_TO_RUN], [DEPRECATED - use --runstatedir=PATH_TO_RUN])]) 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(strict-config-checks, [AS_HELP_STRING([--enable-strict-config-checks], [build with strict configuration checking])]) AC_ARG_ENABLE(hardening, [AS_HELP_STRING([--disable-hardening], [do not build with security hardening])]) AC_ARG_ENABLE(optimise, [AS_HELP_STRING([--enable-optimise], [compiler optimisation level])], [], [enable_optimise=not-specified]) AC_ARG_ENABLE(warnings, [AS_HELP_STRING([--enable-warnings[[=WARNINGS]]], [additional compiler warnings, disable for reduced set])], [], [enable_warnings=yes]) AC_ARG_ENABLE(extra-warnings, [AS_HELP_STRING([--enable-extra-warnings], [extra compiler warnings that will probably produce many warnings])]) AC_ARG_ENABLE(mem-check, [AS_HELP_STRING([--enable-mem-check], [compile with memory alloc checking - e.g. no writes before or after buffer, everything allocated is freed])]) AC_ARG_ENABLE(mem-check-log, [AS_HELP_STRING([--enable-mem-check-log], [compile with memory alloc checking writing to syslog])]) AC_ARG_ENABLE(openssl-mem-check, [AS_HELP_STRING([--enable-openssl-mem-check], [compile with OpenSSL memory alloc checking - e.g. no writes before or after buffer, everything allocated is freed])]) AC_ARG_ENABLE(malloc-check, [AS_HELP_STRING([--enable-malloc-check], [compile with malloc checking - i.e. malloc etc returning NULL])]) AC_ARG_ENABLE(timer-check, [AS_HELP_STRING([--enable-timer-check], [compile with set time logging])]) AC_ARG_ENABLE(fault-flags-check, [AS_HELP_STRING([--enable-fault-flags-check], [compile with checking VRRP fault flags])]) AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug], [compile with most debugging options])]) AC_ARG_ENABLE(netlink-timers, [AS_HELP_STRING([--enable-netlink-timers], [compile with netlink command timers])]) AC_ARG_ENABLE(smtp-alert-debug, [AS_HELP_STRING([--enable-smtp-alert-debug], [compile with smtp-alert debugging])]) AC_ARG_ENABLE(stacktrace, [AS_HELP_STRING([--enable-stacktrace], [compile with stacktrace support])]) AC_ARG_ENABLE(perf, [AS_HELP_STRING([--enable-perf], [compile with perf performance data recording support for vrrp process])]) AC_ARG_ENABLE(sanitize-address, [AS_HELP_STRING([--enable-sanitize-address], [compile with sanitize=address (ASAN) support])]) AC_ARG_ENABLE(sanitize-address-options, [AS_HELP_STRING([--enable-sanitize-address-options], [compile with sanitize=address (ASAN) default options])]) AC_ARG_ENABLE(sanitize-hwaddress, [AS_HELP_STRING([--enable-sanitize-hwaddress], [compile with sanitize=hwaddress (HWASAN) support])]) AC_ARG_ENABLE(sanitize-hwaddress-options, [AS_HELP_STRING([--enable-sanitize-hwaddress-options], [compile with sanitize=hwaddress (HWASAN) default options])]) AC_ARG_ENABLE(sanitize-undefined, [AS_HELP_STRING([--enable-sanitize-undefined], [compile with sanitize=undefined (UBSAN) support])]) AC_ARG_ENABLE(sanitize-undefined-options, [AS_HELP_STRING([--enable-sanitize-undefined-options], [compile with sanitize=undefined (UBSAN) default options])]) AC_ARG_ENABLE(sanitize-memory, [AS_HELP_STRING([--enable-sanitize-memory], [compile with sanitize=memory (MSAN) support])]) AC_ARG_ENABLE(sanitize-memory-options, [AS_HELP_STRING([--enable-sanitize-memory-options], [compile with sanitize=memory (MSAN) default options])]) AC_ARG_ENABLE(sanitize-leak, [AS_HELP_STRING([--enable-sanitize-leak], [compile with sanitize=leak (LSAN) support])]) AC_ARG_ENABLE(sanitize-leak-options, [AS_HELP_STRING([--enable-sanitize-leak-options], [compile with sanitize=leak (LSAN) default options])]) AC_ARG_ENABLE(sanitize-scudo, [AS_HELP_STRING([--enable-sanitize-scudo], [compile with sanitize=scudo support])]) AC_ARG_ENABLE(sanitize-scudo-options, [AS_HELP_STRING([--enable-sanitize-scudo-options], [compile with sanitize=scudo default options])]) AC_ARG_ENABLE(log-file, [AS_HELP_STRING([--enable-log-file], [enable logging to file (-g)])]) AC_ARG_ENABLE(dump-threads, [AS_HELP_STRING([--enable-dump-threads], [compile with thread dumping support])]) AC_ARG_ENABLE(epoll-debug, [AS_HELP_STRING([--enable-epoll-debug], [compile with epoll_wait() debugging support])]) AC_ARG_ENABLE(epoll-thread-dump, [AS_HELP_STRING([--enable-epoll-thread-dump], [compile with epoll thread dumping support])]) AC_ARG_ENABLE(regex-debug, [AS_HELP_STRING([--enable-regex-debug], [compile with regex debugging support])]) AC_ARG_ENABLE(tsm-debug, [AS_HELP_STRING([--enable-tsm-debug], [compile with TSM debugging support])]) AC_ARG_ENABLE(vrrp-fd-debug, [AS_HELP_STRING([--enable-vrrp-fd-debug], [compile with vrrp fd debugging support])]) AC_ARG_ENABLE(recvmsg-debug, [AS_HELP_STRING([--enable-recvmsg-debug], [compile with recvmsg() debugging support])]) AC_ARG_ENABLE(eintr-debug, [AS_HELP_STRING([--enable-eintr-debug], [compile with EINTR debugging support, set to check/not check for EINTR])]) AC_ARG_ENABLE(track-process-debug, [AS_HELP_STRING([--enable-track-process-debug], [compile with track process debugging support, set to log all process connector events])]) AC_ARG_ENABLE(parser-debug, [AS_HELP_STRING([--enable-parser-debug], [compile with parser debugging support])]) AC_ARG_ENABLE(checksum-debug, [AS_HELP_STRING([--enable-checksum-debug], [compile with checksum debugging support])]) AC_ARG_ENABLE(checker-debug, [AS_HELP_STRING([--enable-checker-debug], [compile with checker debugging support])]) AC_ARG_ENABLE(smtp-connect-debug, [AS_HELP_STRING([--enable-smtp-connect-debug], [compile with smtp connect debugging support])]) AC_ARG_ENABLE(mem-err-debug, [AS_HELP_STRING([--enable-mem-err-debug], [compile with MALLOC/FREE error debugging support])]) AC_ARG_ENABLE(script-debug, [AS_HELP_STRING([--enable-script-debug], [compile with script termination debugging support])]) AC_ARG_ENABLE(one-process-debug, [AS_HELP_STRING([--enable-one-process-debug], [compile with all functionality running in a single process])]) AC_ARG_ENABLE(dump-keywords, [AS_HELP_STRING([--enable-dump-keywords], [compile with keyword dumping support])]) AC_ARG_ENABLE(network-timestamp, [AS_HELP_STRING([--enable-network-timestamp], [compile with network timestamp debugging support])]) AC_ARG_ENABLE(asserts, [AS_HELP_STRING([--enable-asserts], [compile with assert() enabled])]) AC_ARG_WITH(fixed-if-type, [AS_HELP_STRING([--with-fixed-if-type=TYPE], [treat interface type TYPE as unchangeable])]) AC_ARG_WITH(default-config-file, AS_HELP_STRING([--with-default-config-file=FILE], [Default configuration file]), [default_config_file="$withval"], [default_config_file=""]) AC_ARG_WITH(default-runtime-options, AS_HELP_STRING([--with-default-runtime-options=OPTIONS], [Default runtime options]), [default_runtime_options="$withval"], [default_runtime_options="-D"]) AC_ARG_ENABLE(profile, [AS_HELP_STRING([--enable-profile], [compile with profiling flags])]) AC_ARG_ENABLE(strict-cast-align, [AS_HELP_STRING([--enable-strict-cast-align], [compile with strict cast alignment warnings])]) AC_ARG_ENABLE(cast-align-checks, [AS_HELP_STRING([--enable-cast-align-checks], [runtime cast alignment checks])]) AC_ARG_ENABLE(cast-via-void, [AS_HELP_STRING([--enable-cast-via-void], [enable pointer cast via void * (default if warns without)])] [AS_HELP_STRING([--disable-cast-via-void], [disable pointer cast via void * (default if does not warn without)])]) AC_ARG_ENABLE(conversion-checks, [AS_HELP_STRING([--enable-conversion-checks], [compile with conversion warnings if sensible])]) AC_ARG_ENABLE(force-conversion-checks, [AS_HELP_STRING([--enable-force-conversion-checks], [compile with conversion warnings])]) AC_ARG_ENABLE(Werror, [AS_HELP_STRING([--enable-Werror], [compile with warnings being errors])]) AC_ARG_WITH([systemdsystemunitdir], [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],, [with_systemdsystemunitdir=auto]) AC_ARG_WITH([dbus-data-dir], [AS_HELP_STRING([--with-dbus-data-dir=DIR], [Directory for Dbus interface files])]) AC_ARG_ENABLE([cflags], [AS_HELP_STRING([--enable-cflags=flags], [additional CFLAGS])]) AC_ARG_ENABLE([cppflags], [AS_HELP_STRING([--enable-cppflags=flags], [additional CPPFLAGS])]) AC_ARG_ENABLE([ldflags], [AS_HELP_STRING([--enable-ldflags=flags], [additional LDFLAGS])]) # Set the kernel headers path if test -n "$kernel_src_path"; then if test ! -d $kernel_src_path/include; then AC_MSG_ERROR([kernel source path $kernel_src_path/include does not exist]) fi if test ! -d $kernel_src_path/include/linux; then AC_MSG_ERROR([kernel source path $kernel_src_path/include does not appear to include linux header files]) fi if test -d $kernel_src_path/include/uapi/linux; then AC_MSG_ERROR([kernel source path $kernel_src_path appears to be an unprocessed kernel source tree]) fi kernelinc="-isystem $kernel_src_path/include" elif test ! -d /usr/include/linux -a \ -d /usr/src/linux/include; then kernelinc="-isystem /usr/src/linux/include" else kernelinc= fi SRC_DIR=`AS_DIRNAME([$0])` AS_IF([test -n "$with_samples_dir"], [ AS_IF([test $with_samples_dir = yes -o $with_samples_dir = no], [AC_MSG_ERROR([--with-samples-dir requires a directory])]) SAMPLES_DIR=$with_samples_dir ],[SAMPLES_DIR="\${sysconfdir}/$PACKAGE/samples"]) AC_SUBST([SAMPLES_DIR]) CPPFLAGS="$kernelinc $CPPFLAGS" AS_IF([test .$enable_clang = .yes ], [ USE_CC=clang AC_CHECK_TOOL([USE_LLD], [lld], [:]) AS_IF([test $USE_LLD != :], [LDFLAGS+=" -fuse-ld=lld -rtlib=compiler-rt"]) ], [ USE_CC="gcc cc" ]) # Checks for programs. AC_PROG_CC($USE_CC) AC_PROG_MAKE_SET AC_PROG_INSTALL AC_PROG_RANLIB AC_PROG_GREP AC_PROG_LN_S AC_PROG_SED AC_CHECK_TOOL(STRIP,strip) AC_CHECK_TOOL(LDD,ldd) m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) ARFLAGS=cr AC_SUBST(ARFLAGS) # Default settings ENABLE_LOG_FILE_APPEND=No # AC_PROG_LIBTOOL # Ensure we don't override FORTIFY_SOURCE ADD_FORTIFY_SOURCE=1 AC_MSG_CHECKING([if _FORTIFY_SOURCE is enabled]) SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -O2" SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS=$(echo $SAV_CPPFLAGS | sed -e "s/ [^ ]*-D *_FORTIFY_SOURCE=[0-9]*//") AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include int main(void) { printf("%d", _FORTIFY_SOURCE); } ]])], [ AC_MSG_RESULT([yes]) ADD_FORTIFY_SOURCE=0 FORTIFY_SOURCE=$(echo | $CC -O3 -dM -E - | grep _FORTIFY_SOURCE | sed -e "s/.* //") ], [ AC_MSG_RESULT([no]) ]) CPPFLAGS=$SAV_CPPFLAGS CFLAGS=$SAV_CFLAGS AS_IF([test $ADD_FORTIFY_SOURCE -eq 1], [ echo $CPPFLAGS | grep -q -- "-D *_FORTIFY_SOURCE=" AS_IF([test $? -eq 0], [ FORTIFY_SOURCE=$(echo $CPPFLAGS | $SED -e "s/.*-D *_FORTIFY_SOURCE=//" -e "s/ .*//") ADD_FORTIFY_SOURCE=0 ], [ SAV_CFLAGS=$CFLAGS SAV_CPPFLAGS=$CPPFLAGS CFLAGS="$CFLAGS -Werror -O2" CPPFLAGS="$SAV_CPPFLAGS -D_FORTIFY_SOURCE=3" AC_COMPILE_IFELSE([ AC_LANG_SOURCE([[ #include int main(void) {} ]])], [FORTIFY_SOURCE=3], [ CPPFLAGS="$SAV_CPPFLAGS -D_FORTIFY_SOURCE=2" AC_COMPILE_IFELSE([ AC_LANG_SOURCE([[ #include int main(void) {} ]])], [FORTIFY_SOURCE=2], [FORTIFY_SOURCE=1]) ]) CPPFLAGS=$SAV_CPPFLAGS CFLAGS=$SAV_CFLAGS ]) ]) # # save the configure arguments # args=`echo $ac_configure_args | $SED -e "s/'//g"` AS_IF([test .$enable_reproducible_build = .yes], [`echo $ac_configure_args | $SED -e "s/-ffile-prefix-map=[[^ ]]*//g"`]) AC_DEFINE_UNQUOTED(KEEPALIVED_CONFIGURE_OPTIONS,"$args", [configure options specified]) AS_IF([test .$enable_lto = .yes], [ AS_IF([test .$enable_clang = .yes], [CFLAGS+=" -flto"], [CFLAGS+=" -flto=auto -ffat-lto-objects"]) ]) # Save the CPPFLAGS, CFLAGS, LDFLAGS and LDLIBS settings for make time KA_CPPFLAGS="$kernelinc -D_GNU_SOURCE $CPPFLAGS" KA_CFLAGS="-g $CFLAGS" KA_LDFLAGS=$LDFLAGS KA_LIBS=$LDLIBS # Set any additional compiler flags AS_IF([test -n "$enable_cflags" -a "$enable_cflags" != no], [add_to_var([KA_CFLAGS], ["$enable_cflags"])]) AS_IF([test -n "$enable_cppflags" -a "$enable_cppflags" != no], [add_to_var([KA_CPPFLAGS], ["$enable_cppflags"])]) AS_IF([test -n "$enable_ldflags" -a "$enable_ldflags" != no], [add_to_var([KA_LDFLAGS], ["$enable_ldflags"])]) # For reporting GCC bugs, uncomment the next two lines #KA_CPPFLAGS="$KA_CPPFLAGS -v -save-temps" #KA_CFLAGS="$KA_CFLAGS -v -save-temps" # To produce mixed source and assembly #KA_CFLAGS="$KA_CFLAGS -Wa,adhln" NEED_LIBDL=No #KA_LIBTOOLFLAGS = # Set up the compiler warnings we want dnl -- Unused warnings - traditional c90-c99-compat c99-c11-compat c++-compat larger-than=4096 long-long packed stringop-overflow switch-default vla vla-larger-than=ANY-SIZE dnl alloca-size-larger-than= (since alloca warning) MAX_FRAME_SIZE=5120 WARNINGS_BASIC="all extra unused strict-prototypes" WARNINGS_STD="abi absolute-value address-of-packed-member alloca alloc-larger-than=4096 alloc-zero arith-conversion array-bounds=2 attribute-alias=2 bad-function-cast c11-c2x-compat cast-align cast-qual chkp date-time disabled-optimization double-promotion duplicated-branches duplicated-cond float-conversion float-equal format-overflow format-security format-signedness format-truncation frame-larger-than=$MAX_FRAME_SIZE implicit-fallthrough=3 init-self inline invalid-pch jump-misses-init logical-op missing-declarations missing-field-initializers missing-include-dirs missing-prototypes nested-externs normalized null-dereference old-style-definition overlength-strings pointer-arith redundant-decls shadow shift-overflow=2 stack-protector strict-overflow=4 stringop-overflow=2 stringop-truncation suggest-attribute=cold suggest-attribute=const suggest-attribute=format suggest-attribute=malloc suggest-attribute=noreturn suggest-attribute=pure sync-nand trampolines undef uninitialized unknown-pragmas unsafe-loop-optimizations unsuffixed-float-constants unused-const-variable=2 unused-macros variadic-macros write-strings" WARNINGS_EXTRA="aggregate-return cast-align=strict conversion format-nonliteral format-overflow=2 format-truncation=2 padded pedantic sign-conversion stack-usage=$MAX_FRAME_SIZE strict-overflow=5 stringop-overflow=3 stringop-overflow=4 switch-enum system-headers traditional-conversion" # We want _GNU_SOURCE defined always add_to_var([CPPFLAGS], [-D_GNU_SOURCE]) # fpclassify() needs -lm add_to_var([KA_LIBS], [-lm]) # Some sanity checks on configure options AS_IF([test .$enable_vrrp = .no], AS_IF([test .$IPTABLES_SILENT == .Yes], [enable_iptables=no]) AS_IF([test .$enable_perf != .], [AC_MSG_ERROR([enable-perf requires vrrp])]) AS_IF([test $with_fixed_if_type], [AC_MSG_ERROR([with-fixed-if-type requires vrrp])]) AS_IF([test .$enable_vrrp_fd_debug != .], [AC_MSG_ERROR([enable-vrrp-fd-debug requires vrrp])]) AS_IF([test .$enable_tsm_debug != .], [AC_MSG_ERROR([enable-tsm-debug requires vrrp])]) AS_IF([test .$enable_recvmsg_debug != .], [AC_MSG_ERROR([enable-recvmsg-debug requires vrrp])]) AS_IF([test .$enable_json != .], [AC_MSG_ERROR([enable-json requires vrrp])]) AS_IF([test .$enable_snmp_vrrp != .], [AC_MSG_ERROR([enable-snmp-vrrp requires vrrp])]) AS_IF([test .$enable_snmp_keepalived != .], [AC_MSG_ERROR([enable-snmp-keepalived requires vrrp])]) AS_IF([test .$enable_snmp_rfc != .], [AC_MSG_ERROR([enable-snmp-rfc requires vrrp])]) AS_IF([test .$enable_snmp_rfcv2 != .], [AC_MSG_ERROR([enable-snmp-rfcv2 requires vrrp])]) AS_IF([test .$enable_snmp_rfcv3 != .], [AC_MSG_ERROR([enable-snmp-rfcv3 requires vrrp])]) AS_IF([test .$enable_dbus != .], [AC_MSG_ERROR([enable-dbus requires vrrp])]) AS_IF([test .$enable_vrrp_auth != .], [AC_MSG_ERROR([disable-vrrp-auth requires vrrp])]) AS_IF([test .$enable_checksum_compat != .], [AC_MSG_ERROR([disable-checksum-compat requires vrrp])]) AS_IF([test .$enable_routes != .], [AC_MSG_ERROR([disable-routes requires vrrp])]) AS_IF([test .$enable_linkbeat != .], [AC_MSG_ERROR([disable-linkbeat requires vrrp])]) AS_IF([test .$enable_bfd != .], [AC_MSG_ERROR([enable-bfd requires vrrp])]) AS_IF([test .$enable_iptables != .no], [AC_MSG_ERROR([disable-iptables requires vrrp])]) AS_IF([test .$enable_track_process != .], [AC_MSG_ERROR([disable-track-process requires vrrp])]) AS_IF([test .$enable_network_timestamp != .], [AC_MSG_ERROR([enable-network-timestamp requires vrrp])]) AS_IF([test .$enable_netlink_timers != .], [AC_MSG_ERROR([enable-netlink-timers requires vrrp])]) ) AS_IF([test .$enable_iptables = .no], AS_IF([test .$enable_libipset != .], [AC_MSG_ERROR([disable-libipset requires vrrp and iptables])]) ) AS_IF([test .$enable_libipset = .no], AS_IF([test .$enable_libipset_dynamic != .], [AC_MSG_ERROR([disable-libipset-dynamic requires ipsets])]) ) AS_IF([test .$enable_snmp_rfc != .yes -a .$enable_snmp_rfcv3 != yes], AS_IF([test .$enable_snmp_reply_v3_for_v2 != .], [AC_MSG_ERROR([disable-snmp-reply-v3-for-v2 requires enable-snmp-rfcv3 or enable-snmp-rfc])]) ) AS_IF([test .$enable_dbus != .yes], AS_IF([test .$enable_dbus_create_instance != .], [AC_MSG_ERROR([enable-dbus-create-instance requires enable-dbus])]) ) AS_IF([test .$enable_lvs = .no], AS_IF([test .$enable_regex != .], [AC_MSG_ERROR([enable-regex requires lvs])]) AS_IF([test .$enable_libnl != .], [AC_MSG_ERROR([disable-libnl requires lvs])]) AS_IF([test .$enable_lvs_syncd != .], [AC_MSG_ERROR([disable-lvs-syncd requires lvs])]) AS_IF([test .$enable_lvs_64bit_stats != .], [AC_MSG_ERROR([disable-lvs-64bit-stats requires lvs])]) AS_IF([test .$enable_fwmark != .], [AC_MSG_ERROR([disable-fwmark requires lvs])]) AS_IF([test .$enable_checker_debug != .], [AC_MSG_ERROR([enable-checker-debug requires lvs])]) AS_IF([test .$enable_openssl_mem_check != .], [AC_MSG_ERROR([enable-openssl-mem-check requires lvs])]) ) AS_IF([test .$enable_libnl = .no], AS_IF([test .$enable_libnl_dynamic != .], [AC_MSG_ERROR([enable-libnl-dynamic requires lvs and libnl])]) ) AS_IF([test .$enable_regex != .yes], AS_IF([test .$enable_regex_timers != .], [AC_MSG_ERROR([enable-regex-timers requires enable-regex])]) AS_IF([test .$enable_regex_debug != .], [AC_MSG_ERROR([enable-regex-debug requires enable-regex])]) ) AS_IF([test .$enable_track_process = .no], AS_IF([test .$enable_track_process_debug != .], [AC_MSG_ERROR([enable-track-process-debug incompatible with disable-track-process])]) ) AS_IF([test .$enable_mem_check != .yes], AS_IF([test .$enable_mem_err_debug != .], [AC_MSG_ERROR([enable-mem-err-debug requires --enable-mem-check])]) ) # --enable-debug enables most debugging if not explicitly disabled, but NOT one-process-debug AS_IF([test .$enable_debug = .yes], [ # If we are debugging, we want to be able to write logs to files AS_IF([test .$enable_log_file = .], [enable_log_file=yes]) AS_IF([test .$enable_asserts = .], [enable_asserts=yes]) AS_IF([test .$enable_epoll_debug = .], [enable_epoll_debug=yes]) AS_IF([test .$enable_epoll_thread_dump = .], [enable_epoll_thread_dump=yes]) AS_IF([test .$enable_eintr_debug = .], [enable_eintr_debug=yes]) AS_IF([test .$enable_parser_debug = .], [enable_parser_debug=yes]) AS_IF([test .$enable_timer_check = .], [enable_timer_check=yes]) AS_IF([test .$enable_smtp_alert_debug = .], [enable_smtp_alert_debug=yes]) AS_IF([test .$enable_smtp_connect_debug = .], [enable_smtp_connect_debug=yes]) AS_IF([test .$enable_dump_keywords = .], [enable_dump_keywords=yes]) AS_IF([test .$enable_script_debug = .], [enable_script_debug=yes]) AS_IF([test .$enable_vrrp != .no], [ AS_IF([test .$enable_vrrp_fd_debug = .], [enable_vrrp_fd_debug=yes]) AS_IF([test .$enable_tsm_debug = .], [enable_tsm_debug=yes]) AS_IF([test .$enable_recvmsg_debug = .], [enable_recvmsg_debug=yes]) AS_IF([test .$enable_track_process_debug = .], [enable_track_process_debug=yes]) AS_IF([test .$enable_checksum_debug = .], [enable_checksum_debug=yes]) AS_IF([test .$enable_netlink_timers = .], [enable_netlink_timers=yes]) AS_IF([test .$enable_network_timestamp = .], [enable_network_timestamp=yes]) AS_IF([test .$enable_fault_flags_check = .], [enable_fault_flags_check=yes]) ]) AS_IF([test .$enable_lvs != .no], [ AS_IF([test .$enable_checker_debug = .], [enable_checker_debug=yes]) AS_IF([test .$enable_regex = .yes], [ AS_IF([test .$enable_regex_timers = .], [enable_regex_timers=yes]) AS_IF([test .$enable_regex_debug = .], [enable_regex_debug=yes]) ]) ]) AS_IF([test .$enable_mem_check = .yes], [ AS_IF([test .$enable_mem_err_debug = .], [enable_mem_err_debug=yes]) ]) ]) dnl --with-run-dir duplicates standard --runstatedir AS_IF([test .$with_run_dir != .], [AC_MSG_ERROR([--with-run-dir is deprecated, use --runstatedir=${with_run_dir}/run instead])]) dnl -- Do we want -Wcast-align=strict? AS_IF([test .$enable_strict_cast_align = .yes], [WARNINGS_STD=`echo $WARNINGS_STD | sed -e "s/cast-align /cast-align=strict /"`]) #### find the actual value for $datadir that we'll end up with ## (I know this is broken and should be done in the Makefile, but ## that's a major pain and almost nobody actually seems to care) ## In fact the "Built Sources Example" section of the automake ## manual suggests using configure as an option. AS_AC_EXPAND(EXPANDED_DATADIR, "$datadir") AS_IF([test .$with_dbus_data_dir != .], [DBUS_DATADIR=$with_dbus_data_dir], [DBUS_DATADIR=$EXPANDED_DATADIR]) AC_SUBST(DBUS_DATADIR) AC_DEFINE_UNQUOTED(DBUS_DATADIR,"$DBUS_DATADIR", [Directory for installing DBUS data files]) dnl We want /run to be the default runstatedir, not ${localstatedir}/run which defaults to /usr/local/var/run dnl We want to know if --runstatedir or equivalent was specified echo "$ac_configure_args" | grep -q "'--r" RUNSTATEDIR_SPECIFIED=$? AS_IF([test $RUNSTATEDIR_SPECIFIED -eq 1], [ echo "$ac_configure_args" | grep -q "'-runstatedir" RUNSTATEDIR_SPECIFIED=$? ]) AS_IF([test $RUNSTATEDIR_SPECIFIED -eq 0], [AS_AC_EXPAND(RUNSTATEDIR, "$runstatedir")], [RUNSTATEDIR=/run]) AC_DEFINE_UNQUOTED(RUNSTATEDIR, "$RUNSTATEDIR", [Directory for runtime state files]) GCC_LTO=no AS_IF([test $CC = gcc], [ dnl -- check if we are using LTO. There is a GCC bug which means that when addattr_l() dnl -- is inlined, GCC (up to at least v11.1.1) ignores RELAX_STRINGOP_OVERFLOW and issues dnl -- -Wstringop-overflow warnings dnl -- I haven't been able to produce a test case to demonstrate this GCC bug, so we can't dnl -- test it here, nor have I been able to report a bug upstream to GCC. AC_MSG_CHECKING([if using GCC Link Time Optimisation]) echo " $CFLAGS " | grep -q -- " -flto=auto " AS_IF([test $? -eq 0], [ AC_MSG_RESULT([yes]) GCC_LTO=yes ], [ AC_MSG_RESULT([no]) ]) ]) AS_IF([test $GCC_LTO = yes], [AC_DEFINE([GCC_LTO_NOINLINE], [ __attribute__((noinline)) ], [Define to not inline if using GCC LTO])], [AC_DEFINE([GCC_LTO_NOINLINE], [], [Define to not inline if using GCC LTO])]) dnl -- ulibc/glibc differences AC_MSG_CHECKING([msghdr.msg_controllen is size_t]) SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include int main(void) { struct msghdr msgh = { .msg_controllen = 0 }; printf("%zu", msgh.msg_controllen); } ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([PRI_MSG_CONTROLLEN], [ "zu" ], [Define to zu if msghdr.msg_controllen is size_t, else u]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([PRI_MSG_CONTROLLEN], [ "u" ], [Define to zu if msghdr.msg_controllen is size_t, else u]) ]) CFLAGS=$SAV_CFLAGS dnl -- printing time types (32 bit Ubuntu uses long long int for time_t timeval tv_sec/tv_usec and timespec tv_sec - but timespec tv_nsec is long int!) dnl -- normally all the fields are long int AC_MSG_CHECKING([time print types]) AH_TEMPLATE([PRI_time_t], [Define for print format for time_t]) AH_TEMPLATE([PRI_tv_sec], [Define for print format for struct timeval tv_sec]) AH_TEMPLATE([PRI_tv_usec], [Define for print format for struct timeval tv_usec]) AH_TEMPLATE([PRI_ts_sec], [Define for print format for struct timespec tv_sec]) AH_TEMPLATE([PRI_ts_nsec], [Define for print format for struct timespec tv_nsec]) SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -Wformat -Wformat-signedness" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ int main(void) { } ]])], [ signs="d u" WARN_SIGN="-Werror=format-signedness" ], [ signs="d" WARN_SIGN="" ]) CFLAGS="$SAV_CFLAGS -Werror=format $WARN_SIGN" for field in t tv.tv_sec tv.tv_usec ts.tv_sec ts.tv_nsec; do for sign in $signs; do for len in "" l ll; do AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include #include int main(void) { time_t t = 1; struct timeval tv = { .tv_sec = 2 }; struct timespec ts = { .tv_sec = 3 }; printf("%$len$sign", $field); } ]])], [ if [[ $field = t ]]; then name=time_t else name=$(echo $field | $SED -e "s/\.tv//") fi AC_DEFINE_UNQUOTED([PRI_$name], [ "$len$sign" ]) ], []) done done done CFLAGS=$SAV_CFLAGS AC_MSG_RESULT([done]) dnl -- Check for diagnostic pragmas in functions - GCC 4.6.0 AC_MSG_CHECKING([diagnostic pragmas in functions]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ int main(void) { _Pragma("GCC diagnostic warning \"-Wall\"") } ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_FUNCTION_DIAGNOSTIC_PRAGMAS_], [ 1 ], [Define to 1 if can have _Pragma GCC diagnostic in functions]) ], [ AC_MSG_RESULT([no]) ]) AM_CONDITIONAL([DEBUG], [test .$enable_debug = .yes]) dnl -- Check for diagnostic push/pop pragmas - GCC 4.6.0 AC_MSG_CHECKING([diagnostic push/pop pragmas]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ int main(void) { _Pragma("GCC diagnostic push") } ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_DIAGNOSTIC_PUSH_POP_PRAGMAS_], [ 1 ], [Define to 1 if can have _Pragma GCC diagnostic push/pop]) ], [ AC_MSG_RESULT([no]) ]) dnl - Set up warnings list AS_IF([test ".$enable_warnings" = .no], [WARNINGS_ENABLED=$WARNINGS_BASIC], [WARNINGS_ENABLED="$WARNINGS_BASIC $WARNINGS_STD" AS_IF([test ".$enable_warnings" != .yes], [WARN_LIST=`echo $enable_warnings | sed -e "s/-W//g"` add_to_var([WARNINGS_ENABLED], ["$WARN_LIST"]) ]) ] ) AS_IF([test .$enable_extra_warnings = .yes], [add_to_var([WARNINGS_ENABLED], ["$WARNINGS_EXTRA"])]) if test "$enable_conversion_checks" = yes; then # Check if we can sensibly enable -Wconversion AC_MSG_CHECKING([for usable -Wconversion]) SAV_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wconversion -O2 -Werror" AS_IF([test $ADD_FORTIFY_SOURCE -eq 1], [CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=$FORTIFY_SOURCE"]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include #include #include #include #include #define VAL 255 static void fun(uint8_t val) { } int main(void) { fd_set set; uint8_t val = 42; unsigned u; bool b; size_t size = 17; char c[2]; char *c_ptr = c; struct rtattr rta; struct rtattr *rta_p = &rta; FD_SET(argc+1, &set); fun(argc == VAL ? VAL : val); // vrrp->lower_prio_no_advert = vrrp->strict_mode ? true : global_data->vrrp_lower_prio_no_advert; u = u ? true : b; size = RTA_LENGTH(size); c_ptr = RTA_DATA(c_ptr); rta_p = RTA_NEXT(rta_p, size); val = (u < 256 ) ? u & 0xff : 0; } ]])], [ AC_MSG_RESULT([yes]) add_to_var([WARNINGS_ENABLED], [conversion]) ], [ AC_MSG_RESULT([no]) AC_MSG_WARN([-Wconversion is not sensible with this compiler. Use --enable-force-conversion-checks to override.]) ]) CFLAGS="$SAV_CFLAGS" elif test "$enable_force_conversion_checks" = yes; then add_to_var([WARNINGS_ENABLED], [conversion]) fi if test "$enable_Werror" = yes; then add_to_var([WARNINGS_ENABLED], [error]) fi CONFIG_WARNINGS=$SRC_DIR/lib/config_warnings.h.in SAV_CFLAGS="$CFLAGS" for WARN in $WARNINGS_ENABLED do AC_MSG_CHECKING([for -W$WARN]) CFLAGS="$SAV_CFLAGS -W$WARN -Werror" WARN_VAR=_HAVE_WARNING_`echo $WARN | tr "a-z=-" "A-Z__"`_ LOCAL_WARN_VAR=HAVE_WARNING_`echo $WARN | sed -e "s/=.*//" | tr "a-z-" "A-Z_"` grep -q "^#undef $WARN_VAR$" $CONFIG_WARNINGS AS_IF([test $? -ne 0], [echo -e "\n/* Define to 1 if -W$WARN in use */\n#undef $WARN_VAR" >>$CONFIG_WARNINGS] ) test `echo $WARN | grep "=[[0-9]][[0-9]]*$"` AS_IF([test $? -eq 0], [ WARN_SHORT=`echo $WARN | sed -e 's/=[[0-9]][[0-9]]*$//'` WARN_VAR_SHORT=_HAVE_WARNING_`echo $WARN_SHORT | tr "a-z=-" "A-Z__"`_ grep -q "^#undef $WARN_VAR_SHORT$" $CONFIG_WARNINGS AS_IF([test $? -ne 0], [echo -e "\n/* Define to 1 if -W$WARN_SHORT in use */\n#undef $WARN_VAR_SHORT" >>$CONFIG_WARNINGS] ) ], [ unset WARN_VAR_SHORT ] ) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ int main(void) { } ]])], [ # gcc 9 removed -Wchkp and doesn't error if it is specified, # but rather outputs: # warning: switch '-Wchkp' is no longer supported # so check for the warning. touch conftest.err grep -q "is no longer supported" conftest.err AS_IF([test $? -ne 0], [ AC_MSG_RESULT([yes]) eval $LOCAL_WARN_VAR=yes add_to_var([KA_CFLAGS], [-W$WARN]) AC_DEFINE_UNQUOTED([$WARN_VAR], [ 1 ]) AS_IF([test -z "$WARN_VAR_SHORT"], [], [AC_DEFINE_UNQUOTED([$WARN_VAR_SHORT], [ 1 ])] ) ], [ AC_MSG_RESULT([no]) eval $LOCAL_WARN_VAR=no ]) ], [ AC_MSG_RESULT([no]) eval $LOCAL_WARN_VAR=no ]) done AS_IF([test .$HAVE_WARNING_STRICT_OVERFLOW = .yes], [ # The following is not supported in gcc 5.4.0 CFLAGS="$CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ _Pragma("GCC diagnostic warning \"-Wstrict-overflow=1\"") int main(void) { } ]])], [AC_DEFINE([_HAVE_PRAGMA_WARN_STRICT_OVERFLOW_1_], [ 1 ], [Define to 1 if _Pragma("GCC diagnostic warning \"-Wstrict-overflow=1\"") supported]) ]) ] ) CFLAGS="$SAV_CFLAGS" # keepalived is not suitable for assuming strict aliasing (it does a lot of type casting) add_to_var([KA_CFLAGS], [-fno-strict-aliasing]) dnl ---- [ Is function __attribute__((error(msg))) supported? ] ---- AC_MSG_CHECKING([for function __attribute__((error(msg))) support]) AC_COMPILE_IFELSE( [ AC_LANG_PROGRAM( [[ #include static int func(int i) __attribute__((error("deliberate-error"))); static int func(int i) { return i * 2; } ]], [[ int i = func(2); printf("%d", i); ]] ) ], [ dnl -- The logic is inverted here. If the attribute is supported, dnl -- the compilation will fail! AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_FUNCTION_ATTRIBUTE_ERROR_], [ 1 ], [Define to 1 if function __attribute__((error(msg))) supported]) ]) SAV_CFLAGS="$CFLAGS" CFLAGS="$SAV_CFLAGS -Werror=attributes" dnl ---- [ Is function __attribute__((warn_unused_result)) supported? ] ---- AC_MSG_CHECKING([for function __attribute__((warn_unused_result)) support]) AC_COMPILE_IFELSE( [ AC_LANG_PROGRAM( [[ #include static int func(int i) __attribute__((warn_unused_result)); static int func(int i) { return i * 2; } ]], [[ func(2); printf("%d", 2); ]] ) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_FUNCTION_WARN_UNUSED_RESULTS_], [ 1 ], [Define to 1 if function __attribute__((warn_unused_results)) supported]) ], [ AC_MSG_RESULT([no]) ]) CFLAGS="$SAV_CFLAGS" dnl ---- [ Do we want stricter configuration checking? ] ---- STRICT_CONFIG=No if test "$enable_strict_config_checks" = yes; then AC_DEFINE([_STRICT_CONFIG_], [ 1 ], [Define to 1 if want stricter configuration checking]) STRICT_CONFIG=Yes add_config_opt([STRICT_CONFIG]) fi AM_CONDITIONAL([WITH_STRICT_CONFIG_CHECKS], [test $STRICT_CONFIG = Yes]) if test "$enable_hardening" != no; then AC_MSG_CHECKING([for PIE support]) SAV_CFLAGS="$CFLAGS" SAV_LDFLAGS="$LDFLAGS" CFLAGS="$CFLAGS -fPIE" if test "${enable_profile}" = yes; then # RHEL 7 and others have a problem with profiling with PIE CFLAGS="$CFLAGS -pg" fi LDFLAGS="$LDFLAGS -pie" AC_LINK_IFELSE([AC_LANG_SOURCE([[ int main(void) { int i = 0; } ]])], AC_MSG_RESULT([yes]) add_to_var([KA_CFLAGS], [-fPIE]) add_to_var([KA_LDFLAGS], [-pie]), AC_MSG_RESULT([no])) CFLAGS=$SAV_CFLAGS LDFLAGS=$SAV_LDFLAGS for FLAG in \ "-Wformat -Werror=format-security" \ "-fexceptions" \ "-fstack-protector-strong" \ "--param=ssp-buffer-size=4" \ "-grecord-gcc-switches" do AC_MSG_CHECKING([for $FLAG support]) CFLAGS="$CFLAGS $FLAG" AC_COMPILE_IFELSE( [AC_LANG_SOURCE([[ ]])], [AC_MSG_RESULT([yes])] add_to_var([KA_CFLAGS], [$FLAG]), [AC_MSG_RESULT([no])]) CFLAGS=$SAV_CFLAGS done AS_IF([test $ADD_FORTIFY_SOURCE -eq 1], [ FLAG="-D_FORTIFY_SOURCE=$FORTIFY_SOURCE" AC_MSG_CHECKING([for $FLAG support]) CFLAGS="$CFLAGS -O2 $FLAG" AC_COMPILE_IFELSE( [AC_LANG_SOURCE([[ ]])], [AC_MSG_RESULT([yes])] add_to_var([KA_CFLAGS], [$FLAG]), [AC_MSG_RESULT([no])]) CFLAGS=$SAV_CFLAGS ]) WL_FLAGS= for FLAG in \ "-z,relro" \ "-z,now" do AC_MSG_CHECKING([for -Wl,$FLAG support]) LDFLAGS="$LDFLAGS -Wl,$FLAG" AC_LINK_IFELSE( [AC_LANG_SOURCE([[ int main(void) { int i = 0; } ]])], [ AC_MSG_RESULT([yes]) WL_FLAGS="$WL_FLAGS -Wl,$FLAG" ], [AC_MSG_RESULT([no])] ) CFLAGS=$SAV_CFLAGS LDFLAGS=$SAV_LDFLAGS done if test -n "$WL_FLAGS"; then add_to_var([KA_LDFLAGS], [$WL_FLAGS]) fi fi # enable-optimise AS_IF([test "$enable_optimise" = yes -o "$enable_optimise" = not-specified], [optimise_level=2], [optimise_level=$enable_optimise]) AS_IF([test "$enable_optimise" = no], [optimise_level=0]) AS_IF([test "$optimise_level" -eq 0], [ echo $KA_CFLAGS | $GREP -q -- "-D_FORTIFY_SOURCE=[[^0]]" AS_IF([test $? -eq 0], [AC_MSG_WARN([--disable-optimise requires --disable-hardening])]) ]) FLAG="-O$optimise_level" AC_MSG_CHECKING([for $FLAG support]) SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $FLAG" AC_COMPILE_IFELSE( [AC_LANG_SOURCE([[ ]])], [AC_MSG_RESULT([yes])] add_to_var([KA_CFLAGS], [$FLAG]), [ AC_MSG_RESULT([no]) AS_IF([test "$enable_optimise" != not-specified], [AC_MSG_ERROR([Invalid optimisation level specified])]) ]) CFLAGS=$SAV_CFLAGS AC_SUBST(KA_CPPFLAGS) AC_SUBST(KA_CFLAGS) AC_SUBST(KA_LDFLAGS) AC_SUBST(KA_LIBS) # AC_SUBST(KA_LIBTOOLFLAGS) # Check for fallthrough attribute echo CFLAGS=$KA_CFLAGS SAV_CFLAGS=$CFLAGS CFLAGS="$KA_CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ int main(int argc, char **argv) { switch(argc) { case 1: argv[0][0] = argv[0][1]; [[fallthrough]]; case 2: argv[1][0] = argv[1][1]; break; } } ]])], [ AC_DEFINE([__fallthrough], [ [[[fallthrough]]] ], [Define fallthrough attribute]) ], [ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ int main(int argc, char **argv) { switch(argc) { case 1: argv[0][0] = argv[0][1]; __attribute__((fallthrough)); case 2: argv[1][0] = argv[1][1]; break; } } ]])], [AC_DEFINE([__fallthrough], [ __attribute__((fallthrough)) ], [Define fallthrough attribute])], [AC_DEFINE([__fallthrough], [ ], [Define fallthrough attribute])]) ]) CFLAGS=$SAV_CFLAGS # Check if unaligned memory access is supported (for ARM not supported prior to ARMv6 processors) AC_MSG_CHECKING([for unaligned memory access]) AC_RUN_IFELSE( [ AC_LANG_PROGRAM( [[ #include #include #if __BYTE_ORDER == __BIG_ENDIAN #if ULONG_MAX == 0xffffffffffffffffUL #define CHK_VAL 0x1234567890abcdefUL #elif ULONG_MAX == 0xffffffffUL #define CHK_VAL 0x12345678UL #else #define CHK_VAL 0x1234UL #endif #elif __BYTE_ORDER == __LITTLE_ENDIAN #if ULONG_MAX == 0xffffffffffffffffUL #define CHK_VAL 0xefcdab9078563412UL #elif ULONG_MAX == 0xffffffffUL #define CHK_VAL 0x78563412UL #else #define CHK_VAL 0x3412UL #endif #else #error Neither big nor little endian - unsupported #endif ]], [[ unsigned long arr[2] = { 0, 0 }; unsigned char *p = (unsigned char *)arr + 1; unsigned i; *(unsigned long *)p = CHK_VAL; return !!(arr[0] == CHK_VAL || p[0] != 0x12 || p[1] != 0x34 || p[2] != 0x56); ]] ) ], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([_NO_UNALIGNED_ACCESS_], [ 1 ], [Define to 1 if unaligned memory access not supported]) ], [ AC_MSG_RESULT([unknown]) AC_MSG_WARN([Cannot determine if unaligned access supported. Assuming yes.]) ] ) # Check if unaligned memory access creates warnings AC_MSG_CHECKING([for unaligned memory access causes warnings]) UNALIGNED_ACCESS_WARNED=0 SAV_CFLAGS="$CFLAGS" CFLAGS=`echo $KA_CFLAGS | sed -e "s/.*\(-Wcast-align[[^ ]]*\) .*/\1/"` CFLAGS="$CFLAGS -Werror" AC_COMPILE_IFELSE( [ AC_LANG_PROGRAM( [], [[ char c; int i = *(int *)&c; ]] ) ], [AC_MSG_RESULT([no])], [ AC_MSG_RESULT([yes]) UNALIGNED_ACCESS_WARNED=1 ], [ AC_MSG_RESULT([unknown]) AC_MSG_WARN([Cannot determine if unaligned access supported. Assuming yes.]) ] ) CFLAGS="$SAV_CFLAGS" dnl - Do we want/need to cast via a void *? AS_IF([test .$enable_cast_via_void = .no], [], [ AS_IF([test .$enable_cast_via_void = .yes -o $UNALIGNED_ACCESS_WARNED -eq 1], AC_DEFINE([CAST_VIA_VOID], [ 1 ], [Define to 1 to cast via void *])) ] ) dnl -- Runtime check cast alignments? AS_IF([test .$enable_cast_align_checks = .yes], AC_DEFINE([CHECK_CAST_ALIGN], [ 1 ], [Define to 1 to do runtime cast alignment checks])) # Checks for libraries. dnl clock_gettime() required -lt before glibc 2.17 AC_MSG_CHECKING([for clock_gettime() requires -lrt]) AC_LINK_IFELSE([AC_LANG_SOURCE([[ #include int main(void) { int i; struct timespec ts; i = clock_gettime(CLOCK_MONOTONIC, &ts); } ]])], [AC_MSG_RESULT([no])], [ SAV_LIBS="$LIBS" LIBS="$LIBS -lrt" AC_LINK_IFELSE([AC_LANG_SOURCE([[ #include int main(void) { int i; struct timespec ts; i = clock_gettime(CLOCK_MONOTONIC, &ts); } ]])], [AC_MSG_RESULT([yes])] add_to_var([KA_LIBS], [-lrt]), [AC_MSG_ERROR([clock_gettime() not supported])]) LIBS=$SAV_LIBS ]) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/ioctl.h sys/param.h sys/prctl.h sys/socket.h sys/time.h syslog.h unistd.h], [], [AC_MSG_ERROR([Missing/unusable system header file <$ac_header>])]) AS_IF([test .$enable_lvs != .no], [AC_CHECK_HEADERS([net/if_arp.h], [], [AC_MSG_ERROR([Missing/unusable <$ac_header> - is glibc headers package installed?])]) ]) # check for kernel headers SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" AC_CHECK_HEADERS([linux/types.h], [], [AC_MSG_ERROR([Missing/unusable kernel header file <$ac_header> - is kernel headers package installed?])]) AS_IF([test .$enable_vrrp != .no], [ AC_CHECK_HEADERS([linux/ethtool.h linux/if_ether.h linux/if_packet.h linux/ip.h linux/sockios.h], [], [AC_MSG_ERROR([Missing/unusable kernel header file <$ac_header> - is kernel headers package installed?])]) AC_CHECK_HEADERS([linux/fib_rules.h linux/if_addr.h linux/if_link.h], [], [AC_MSG_ERROR([Missing/unusable kernel header file <$ac_header> - is kernel headers package installed?])], [[$NETLINK_EXTRA_INCLUDE]]) ]) AS_IF([test .$enable_lvs != .no], [ AC_CHECK_HEADERS([linux/icmp.h linux/icmpv6.h linux/errqueue.h], [], [AC_MSG_ERROR([Missing/unusable kernel header file <$ac_header> - is kernel headers package installed?])]) dnl -- linux/errqueue.h need sys/time.h before Linux < v5.1 AC_MSG_CHECKING([linux/errqueue.h needs sys/time.h]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include int main(void) { } ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([ERRQUEUE_NEEDS_SYS_TIME], [ 1 ], [Define to 1 if linux/errqueue.h needs sys/time.h]) ]) ]) CPPFLAGS="$SAV_CPPFLAGS" # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL AC_C_INLINE AC_TYPE_INT64_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T AC_C_CONST # Checks for library functions. AC_FUNC_FORK # We don't want the following two, since autoconf, if malloc(0) returns NULL, refines malloc as rpl_malloc # and we have to provide our own rpl_malloc() and likewise rpl_realloc() functions. # keepalived doesn't do 0 length malloc()s so it is not an issue. # We add malloc and realloc to AC_CHECK_FUNCS instead. #AC_FUNC_MALLOC #AC_FUNC_REALLOC dnl - dup3 since Linux 2.6.27 and glibc 2.9 AC_CHECK_FUNCS([dup2 dup3 getcwd gettimeofday malloc memmove memset realloc select setenv socket strcasecmp strchr strdup strerror strpbrk strstr strtol strtoul uname]) dnl - vsyslog() Not defined by Posix, but available in glibc and musl AC_CHECK_FUNCS([vsyslog], [add_system_opt([VSYSLOG])]) dnl - memfd_create() - since Linux 3.17 and glibc 2.27 dnl - memfd_create() appeared in the kernel long before it appeared in dnl - glibc so we use the raw syscall if it is available AC_CHECK_FUNCS([memfd_create], [add_system_opt([MEMFD_CREATE])]) AS_IF([test "x$ac_cv_func_memfd_create" != xyes], [ AC_CHECK_DECLS([__NR_memfd_create], [AC_DEFINE([USE_MEMFD_CREATE_SYSCALL], [ 1 ], [Use syscall for memfd_create])], [], [[#include ]]) ]) dnl - Since Linux 3.17 AC_CHECK_DECLS([O_TMPFILE], [], [], [[#include ]]) # glibc uses unsigned int as 3rd parameter to __assert_fail(), musl uses int. AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include void __assert_fail(const char * a, const char *b, unsigned int l, const char *c) { exit(a[0] + b[0] + c[0] + l == 0); } ]])], [LINE_type="unsigned int"], [LINE_type="int"]) AC_DEFINE_UNQUOTED([LINE_type], [ $LINE_type ], [The type of parameter __line passed to __assert_fail()]) # If sizeof(time_t) is only 4 bytes, then adding two times after Sat 10 Jan 13:37:04 GMT 2004 # results in overflow. AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [[#include ]], [[ /* This is a bit of a trick. If sizeof(time_t) <= 4, then the size of the array is negative, giving a compile error */ char arr[(int)sizeof(time_t) - 5]; ]])], [], [AC_DEFINE([TIME_T_ADD_OVERFLOWS], [ 1 ], [Define to 1 if sizeof(time_t) is 4 bytes])]) dnl Check if libc supports __always_inline dnl See glibc sys/cdefs.h definition of __always_inline and comment SAV_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wattributes -Werror" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include static __always_inline int test_func(int val) { return val; } int main(void) { int val = test_func(3); return val; } ]])], [], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ static __inline __attribute__ ((__always_inline__)) int test_func(int val) { return val; } int main(void) { int val = test_func(3); return val; } ]])], [AC_DEFINE([__always_inline], [__inline __attribute__ ((__always_inline__))], [Define __always_inline if libc does not define it])], [AC_DEFINE([__always_inline], [inline], [Define __always_inline if libc does not define it])] )]) CFLAGS="$SAV_CFLAGS" dnl - Do we want to override dynamic/static linking? AS_IF([test "$enable_dynamic_linking"], [ AS_IF([test .$enable_vrrp != .no], [ enable_libiptc_dynamic=$enable_dynamic_linking enable_libipset_dynamic=$enable_dynamic_linking ]) AS_IF([test .$enable_lvs != .no], [ enable_libnl_dynamic=$enable_dynamic_linking ]) ]) # check for missing definition - added in glibc 2.8 AC_CHECK_DECLS([ETHERTYPE_IPV6], [], [ AC_DEFINE([ETHERTYPE_IPV6], [0x86dd], [Defined here if not found in .]) ], [[#include ]]) # IPV6_FREEBIND - added Linux v4.15 AS_IF([test .$enable_vrrp != .no], [ AC_CHECK_DECLS([IPV6_FREEBIND], [add_system_opt([IPV6_FREEBIND])], [], [[#include ]]) ]) # IPV6_MULTICAST_ALL - added Linux v4.20 AC_CHECK_DECLS([IPV6_MULTICAST_ALL], [ add_system_opt([IPV6_MULTICAST_ALL]) # We can't include before otherwise there are a lot of # duplicate definitions. If we include them the other way around and # doesn't define IPV6_MULTICAST_ALL, then we don't get the definition from # due to the UAPI guards to stop compile errors. # IPV6_MULTICAST_ALL is defined in , and if it isn't defined in # we must create our own definition. AC_COMPILE_IFELSE([AC_LANG_SOURCE( [ #include int main(void) { int val = IPV6_MULTICAST_ALL; } ])], [], [ # Find the defined value of IPV6_MULTICAST_ALL INCLUDE_FILES=`echo -e "#include \n int main(void) { int var = IPV6_MULTICAST_ALL; }" | $CC -H -E - -o/dev/null 2>&1 | sed -e "s/^[.]*//" | grep "^ "` IPV6_MCA=`grep "#define.*IPV6_MULTICAST_ALL" $INCLUDE_FILES | sed -e "s/.*IPV6_MULTICAST_ALL[[ \t]]*//"` AC_DEFINE_UNQUOTED([IPV6_MULTICAST_ALL], [$IPV6_MCA], [Defined in linux/in6.h but not in netinet/in.h]) ]) ], [], [[#include ]]) dnl ----[ Checks for openssl ]---- # check for openssl headers NEED_EVP=no NEED_SSL=no if test "$enable_vrrp" != no -a \ "$enable_vrrp_auth" != no; then NEED_EVP=yes fi if test "$enable_lvs" != no; then NEED_EVP=yes NEED_SSL=yes fi AC_CHECK_HEADERS([openssl/ssl.h openssl/err.h], [], [ if test $NEED_SSL = yes; then AC_MSG_ERROR([ !!! OpenSSL is not properly installed on your system. !!! !!! Can not include OpenSSL headers files. !!!]) fi ]) AC_CHECK_HEADERS([openssl/md5.h openssl/evp.h], [], [ if test $NEED_EVP = yes; then AC_MSG_ERROR([ !!! OpenSSL is not properly installed on your system. !!! !!! Can not include OpenSSL EVP headers files. !!!]) fi ]) unset LIBS $PKG_CONFIG --exists openssl if test $? -eq 0; then add_pkg_config([openssl], [OPENSSL]) else OPENSSL_LIBS="-lssl -lcrypto" fi EXTRA_LIBS=`echo $OPENSSL_LIBS | sed -e "s/-lssl//"` AC_CHECK_LIB(ssl, SSL_CTX_new, [], [ if test $NEED_SSL = yes; then AC_MSG_ERROR([OpenSSL libraries are required]) fi ], [$EXTRA_LIBS]) if test $NEED_SSL = yes; then add_to_var([KA_LIBS], [$LIBS]) fi unset LIBS EXTRA_LIBS=`echo $OPENSSL_LIBS | sed -e "s/-lcrypto//"` AC_CHECK_LIB(crypto, EVP_DigestInit_ex, [], [ if test $NEED_EVP = yes; then AC_MSG_ERROR([OpenSSL EVP libraries are required]) fi ], [$EXTRA_LIBS]) if test $NEED_EVP = yes; then add_to_var([KA_LIBS], [$LIBS]) AC_CHECK_LIB(crypto, EVP_MD_CTX_new, [], [ AC_DEFINE([EVP_MD_CTX_new()], [EVP_MD_CTX_create()], [Named changed in OpenSSL v1.1.0]) AC_DEFINE([EVP_MD_CTX_free(ctx)], [EVP_MD_CTX_destroy(ctx)], [Named changed in OpenSSL v1.1.0]) AC_DEFINE([EVP_MD_CTX_reset(ctx)], [EVP_MD_CTX_init(ctx)], [Named changed in OpenSSL v1.1.0]) ], [$EXTRA_LIBS]) fi unset LIBS # Introduced in OpenSSL ver 0.9.9 LIBS=$OPENSSL_LIBS AC_MSG_CHECKING([SSL_set_tlsext_host_name() - may be a definition]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include int main(void) { request_t req; SSL_set_tlsext_host_name(req.ssl, "SSL"); } ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_SSL_SET_TLSEXT_HOST_NAME_], [ 1 ], [Define to 1 if have SSL_set_tlsext_host_name()]) ]) # SSL_CTX_set_verify_depth() introduced OpenSSL v0.9.5a AC_CHECK_FUNCS([SSL_CTX_set_verify_depth]) # SSL_set0_rbio(), SSL_set0_wbio() OPENSSL_init_crypto() and TLS_method() introduced OpenSSL v1.1.0 AC_CHECK_FUNCS([SSL_set0_rbio SSL_set0_wbio OPENSSL_init_crypto TLS_method]) # ERR_get_error_all introduced in OpenSSL v3.0 AC_CHECK_FUNCS([ERR_get_error_all]) # In OpenSSL v1.1.1 the call to SSL_CTX_new() fails if OPENSSL_init_crypto() has been called with # OPENSSL_INIT_NO_LOAD_CONFIG. It does not fail in v1.1.0h and v1.1.1b. AS_IF([test .$ac_cv_func_OPENSSL_init_crypto = .yes], [ AS_IF([test .$ac_cv_func_TLS_method = .yes], [method_func=TLS_method], [method_func=SSLv23_method]) AC_RUN_IFELSE( [AC_LANG_PROGRAM( [[#include ]], [[ const SSL_METHOD *meth; SSL_CTX *ctx; if (!OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG, NULL)) return 1; /* Initialize SSL context */ meth = $method_func(); if (!(ctx = SSL_CTX_new(meth))) return 1; return 0; ]])], [openssl_init_no_load_bug=0], [openssl_init_no_load_bug=1], [ AC_MSG_WARN([Cannot determine if need to OPENSSL_init_crypto() problem. Assuming yes for safety.]) openssl_init_no_load_bug=1 ] ) AS_IF([test $openssl_init_no_load_bug -eq 1], [AC_DEFINE([HAVE_OPENSSL_INIT_NO_LOAD_CONFIG_BUG], [ 1 ], [Define to 1 if OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG) bug)])]) ]) unset LIBS dnl ----[ Check if libkmod available ]---- $PKG_CONFIG --exists libkmod AS_IF([ test $? -eq 0 ], [ AC_DEFINE([_HAVE_LIBKMOD_], [ 1 ], [Define to 1 if have libkmod]) add_pkg_config([libkmod]) add_system_opt([LIBKMOD]) ]) dnl ----[ Check for IPv4 devconf netlink support ]---- IPV4_DEVCONF=No if test .$enable_vrrp != .no; then dnl ----[Check have IPV4_DEVCONF defines - since Linux 3.11]---- SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" IPV4_DEVCONF=Yes AC_CHECK_DECLS([ IPV4_DEVCONF_ARP_IGNORE, IPV4_DEVCONF_ACCEPT_LOCAL, IPV4_DEVCONF_RP_FILTER, IPV4_DEVCONF_ARPFILTER], [], [ IPV4_DEVCONF=No break ], [[#include ]]) if test $IPV4_DEVCONF = Yes; then AC_DEFINE([_HAVE_IPV4_DEVCONF_], [ 1 ], [Define to 1 if have IPv4 netlink device configuration]) add_system_opt([IPV4_DEVCONF]) fi AC_CHECK_HEADERS([linux/rtnetlink.h], [], [AC_MSG_ERROR([Unusable linux/rtnetlink.h])], [$RTNETLINK_EXTRA_INCLUDE]) CPPFLAGS="$SAV_CPPFLAGS" fi dnl ----[ Checks for libraries ]---- NETLINK_VER=0 IPVS_USE_NL=No if test .$enable_lvs != .no -a .${enable_libnl} != .no; then dnl -- IPVS netlink support since linux 2.6.28 - 24/12/08 $PKG_CONFIG --exists libnl-3.0 if test $? -eq 0; then add_pkg_config([libnl-3.0], [NL3], [remove-requires]) AC_CHECK_LIB($NL3_LIB_NAMES, nl_socket_alloc, [ NETLINK_VER=3 AC_DEFINE([_HAVE_LIBNL3_], [ 1 ], [Define to 1 if using libnl-3]) add_system_opt([LIBNL3]) if test .$enable_libnl_dynamic = .yes; then add_system_opt([LIBNL_DYNAMIC]) add_pkg_config_without_libs([libnl-3.0]) AC_DEFINE([_LIBNL_DYNAMIC_], [ 1 ], [Define to 1 if building with libnl dynamic linking]) NEED_LIBDL=Yes get_lib_name([$NL3_LIB_NAMES], [nl_socket_alloc]) AC_DEFINE_UNQUOTED([NL3_LIB_NAME], [ "$LIB_NAME" ], [Define the nl-3 library name]) else add_pkg_config([libnl-3.0]) fi add_pkg_config([libnl-genl-3.0], [GENL], [remove-requires]) AC_CHECK_LIB($GENL_LIB_NAMES, genl_connect, [], [AC_MSG_ERROR([libnl-3 is installed but not libnl-gen-3. Please, install libnl-gen-3/libnl-genl-3.])], [$NL3_LIBS]) IPVS_USE_NL=Yes if test .$enable_libnl_dynamic = .yes; then add_pkg_config_without_libs([libnl-genl-3.0]) get_lib_name([$GENL_LIB_NAMES], [genl_connect]) AC_DEFINE_UNQUOTED([NL3_GENL_LIB_NAME], [ "$LIB_NAME" ], [Define the nl-genl-3.0 library name]) else add_pkg_config([libnl-genl-3.0]) fi ], []) fi if test $NETLINK_VER -eq 0; then AC_CHECK_LIB(nl, nl_socket_modify_cb, [ IPVS_USE_NL=Yes NETLINK_VER=1 AC_DEFINE([_HAVE_LIBNL1_], [ 1 ], [Define to 1 if using libnl-1]) add_system_opt([LIBNL1]) if test .$enable_libnl_dynamic = .yes; then add_pkg_config_without_libs([libnl-1]) add_config_opt([LIBNL_DYNAMIC]) AC_DEFINE([_LIBNL_DYNAMIC_], [ 1 ], [Define to 1 if building with libnl dynamic linking]) NEED_LIBDL=Yes get_lib_name([nl], [nl_socket_modify_cb]) AC_DEFINE_UNQUOTED([NL_LIB_NAME], [ "$LIB_NAME" ], [Define the nl library name]) else add_pkg_config([libnl-1]) fi ], [AC_MSG_WARN([keepalived will be built without libnl support.]) ]) fi if test $NETLINK_VER -ne 0; then SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernel_inc $NL3_CPPFLAGS" AC_CHECK_HEADERS([netlink/netlink.h], [], [AC_MSG_ERROR([netlink headers missing])]) AC_CHECK_HEADERS([netlink/genl/ctrl.h netlink/genl/genl.h], [], [AC_MSG_ERROR([netlink genl headers missing])]) CPPFLAGS="$SAV_CPPFLAGS" fi fi AM_CONDITIONAL([LIBNL1], [test $NETLINK_VER -eq 1]) AM_CONDITIONAL([LIBNL3], [test $NETLINK_VER -eq 3]) AM_CONDITIONAL([LIBNL_DYNAMIC], [test .$enable_lvs != .no -a .$enable_libnl_dynamic = .yes -a $NETLINK_VER -ne 0]) unset LIBS MAGIC=0 AC_CHECK_LIB(magic, magic_open, [ AC_DEFINE([_HAVE_LIBMAGIC_], [ 1 ], [Define to 1 if have magic library]) add_to_var([KA_LIBS], [-lmagic]) MAGIC=1 ]) AM_CONDITIONAL([MAGIC], [test $MAGIC -eq 1]) unset LIBS dnl -- Check for the following definitions introduced at various times into Linux dnl --RTAX_QUICKACK dnl -- Linux 3.11 dnl --FRA_SUPPRESS_PREFIXLEN dnl -- Linux 3.12 dnl --FRA_SUPPRESS_IFGROUP dnl -- Linux 3.12 dnl --RTAX_CC_ALGO dnl -- Linux 4.0 dnl --RTA_VIA dnl -- Linux 4.1 dnl --RTA_NEWDST dnl -- Linux 4.1 dnl --RTA_PREF dnl -- Linux 4.1 dnl --FRA_TUN_ID dnl -- Linux 4.3 dnl --RTA_ENCAP dnl -- Linux 4.3 dnl --RTEXT_FILTER_SKIP_STATS dnl -- Linux 4.4 dnl --RTA_EXPIRES dnl -- Linux 4.5 dnl --FRA_L3MDEV dnl -- Linux 4.8 dnl --FRA_UID_RANGE dnl -- Linux 4.10 dnl --RTAX_FASTOPEN_NO_COOKIE dnl -- Linux 4.15 dnl --FRA_PROTOCOL dnl -- Linux 4.17 dnl --FRA_IP_PROTO dnl -- Linux 4.17 dnl --FRA_SPORT_RANGE dnl -- Linux 4.17 dnl --FRA_DPORT_RANGE dnl -- Linux 4.17 dnl --RTA_TTL_PROPAGATE dnl -- Linux 4.12 AC_CHECK_DECLS([RTA_ENCAP, RTA_EXPIRES, RTA_NEWDST, RTA_PREF, FRA_SUPPRESS_PREFIXLEN, FRA_SUPPRESS_IFGROUP, FRA_TUN_ID, RTAX_CC_ALGO, RTAX_QUICKACK, RTEXT_FILTER_SKIP_STATS, FRA_L3MDEV, FRA_UID_RANGE, RTAX_FASTOPEN_NO_COOKIE, RTA_VIA, FRA_PROTOCOL, FRA_IP_PROTO, FRA_SPORT_RANGE, FRA_DPORT_RANGE, RTA_TTL_PROPAGATE], [], [], [[$RTNETLINK_EXTRA_INCLUDES #include #include #include ]]) for flag in RTA_ENCAP RTA_EXPIRES RTA_NEWDST RTA_PREF FRA_SUPPRESS_PREFIXLEN FRA_SUPPRESS_IFGROUP FRA_TUN_ID RTAX_CC_ALGO RTAX_QUICKACK RTEXT_FILTER_SKIP_STATS FRA_L3MDEV FRA_UID_RANGE RTAX_FASTOPEN_NO_COOKIE RTA_VIA FRA_PROTOCOL FRA_IP_PROTO FRA_SPORT_RANGE FRA_DPORT_RANGE RTA_TTL_PROPAGATE; do AS_VAR_COPY([decl_var], [ac_cv_have_decl_$flag]) if test ${decl_var} = yes; then add_system_opt[${flag}] fi done dnl - Introduced in Linux 3.14 AC_CHECK_DECLS([IFA_FLAGS], [], [], [[#include ]]) for flag in IFA_FLAGS; do AS_VAR_COPY([decl_var], [ac_cv_have_decl_$flag]) if test ${decl_var} = yes; then add_system_opt[${flag}] fi done dnl -- Linux 3.15 AC_CHECK_DECLS([F_OFD_SETLK], [], [], [[ #include #include ]]) for flag in F_OFD_SETLK; do AS_VAR_COPY([decl_var], [ac_cv_have_decl_$flag]) if test ${decl_var} = yes; then add_system_opt[${flag}] 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], [ AC_CHECK_DECLS([LWTUNNEL_ENCAP_MPLS, LWTUNNEL_ENCAP_ILA], [], [], [[#include ]]) for flag in LWTUNNEL_ENCAP_MPLS LWTUNNEL_ENCAP_ILA; do AS_VAR_COPY([decl_var], [ac_cv_have_decl_$flag]) if test ${decl_var} = yes; then add_system_opt([${flag}]) fi done ]) dnl ----[Check for iptables libraries]---- USE_IPTABLES=No USE_LIBIPSET=No AS_IF([test .$enable_iptables != .no], [ USE_IPTABLES=Yes SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" AC_CHECK_HEADERS([libiptc/libip6tc.h libiptc/libiptc.h libiptc/libxtc.h], [], [ USE_IPTABLES=No break ]) CPPFLAGS="$SAV_CPPFLAGS" if test $USE_IPTABLES = Yes; then PKG_CONFIG_IP4TC=Yes $PKG_CONFIG --exists libip4tc AS_IF([test $? -eq 0], [ add_pkg_config([--static libip4tc], [IP4TC], [remove-requires]) add_pkg_config([--static libip6tc], [IP6TC], [remove-requires]) IPTC_LIBS="$IP4TC_LIBS $IP6TC_LIBS" IPTC_LIB_NAMES="$IP4TC_LIB_NAMES $IP6TC_LIB_NAMES" ], [ PKG_CONFIG_IP4TC=No add_pkg_config([--static libiptc], [IPTC], [remove-requires]) ]) SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" AC_SEARCH_LIBS(iptc_init, $IPTC_LIB_NAMES, [ if test .${enable_libiptc_dynamic} != .yes; then AS_IF([test $PKG_CONFIG_IP4TC = Yes], [ add_pkg_config([--static libip4tc]) add_pkg_config([--static libip6tc]) ], [add_pkg_config([--static libiptc])]) dnl - Older versions of libiptc produced a requirement for -liptc, but we don't need it KA_LIBS=`echo $KA_LIBS | sed -e "s/ -liptc//"` dnl - Even older versions of libiptc don't produce any requirement other than -liptc IPTC_LIBS=`echo $IPTC_LIBS | sed -e "s/ *-L[[^ ]]* */ /" -e "s/ *-liptc */ /" -e "s/^ *$//"` if test ".$IPTC_LIBS" = .; then KA_LIBS="$KA_LIBS -lip4tc -lip6tc" fi else AS_IF([test $PKG_CONFIG_IP4TC = Yes], [ add_pkg_config_without_libs([libip4tc]) add_pkg_config_without_libs([libip6tc]) ], [ add_pkg_config_without_libs([libiptc]) ]) add_config_opt([LIBIPTC_DYNAMIC]) AC_DEFINE([_LIBIPTC_DYNAMIC_], [ 1 ], [Define to 1 if building with libiptc dynamic linking]) NEED_LIBDL=Yes AC_SEARCH_LIBS(ip6tc_init, $IPTC_LIB_NAMES) IP4TC_NAME=`echo $ac_cv_search_iptc_init | sed -e "s/-l//"` IP6TC_NAME=`echo $ac_cv_search_ip6tc_init | sed -e "s/-l//"` get_lib_name([$IP4TC_NAME], [iptc_init]) AC_DEFINE_UNQUOTED([IP4TC_LIB_NAME], [ "$LIB_NAME" ], [Define the ip4tc library name]) get_lib_name([$IP6TC_NAME], [ip6tc_init]) AC_DEFINE_UNQUOTED([IP6TC_LIB_NAME], [ "$LIB_NAME" ], [Define the ip6tc library name]) LIBIPTC_DYNAMIC=Yes fi ], [USE_IPTABLES=No]) CPPFLAGS="$SAV_CPPFLAGS" fi if test $USE_IPTABLES = Yes; then dnl ----[Check for ipset libraries]---- SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" if test "${enable_libipset}" != no; then $PKG_CONFIG --exists libipset if test $? -eq 0; then add_pkg_config([libipset], [IPSET], [remove-requires]) else IPSET_LIBS="-lipset" IPSET_LIB_NAMES=ipset fi SAV_LIBS=$LIBS AC_SEARCH_LIBS(ipset_session_init, $IPSET_LIB_NAMES, [ USE_LIBIPSET=Yes AC_CHECK_HEADERS([libipset/data.h libipset/linux_ip_set.h libipset/session.h libipset/types.h], [], [ USE_LIBIPSET=No break ]) if test $USE_LIBIPSET = Yes; then AC_DEFINE([_HAVE_LIBIPSET_], [ 1 ], [Define to 1 if have ipset library]) $PKG_CONFIG --exists libipset if test $? -eq 0; then if test .${enable_libipset_dynamic} = .no; then add_pkg_config([libipset]) else add_pkg_config_without_libs([libipset]) fi elif test .${enable_libipset_dynamic} = .no; then add_to_var([KA_LIBS], [$ac_cv_search_ipset_session_init]) fi if test .${enable_libipset_dynamic} != .no; then AC_DEFINE([_LIBIPSET_DYNAMIC_], [ 1 ], [Define to 1 if building with libipset dynamic linking]) add_config_opt([LIBIPSET_DYNAMIC]) NEED_LIBDL=Yes LIBIPSET_NAME=`echo $ac_cv_search_ipset_session_init | sed -e "s/-l//"` get_lib_name([$LIBIPSET_NAME], [ipset_session_init]) AC_DEFINE_UNQUOTED([IPSET_LIB_NAME], [ "$LIB_NAME" ], [Define the ipset library name]) else add_config_opt([LIBIPSET]) fi dnl -- xt_set_info_match_v4 declared since Linux 3.19 AC_CHECK_MEMBER([struct xt_set_info_match_v4.match_set.index], [AC_DEFINE([HAVE_XT_SET_INFO_MATCH_V4], [ 1 ], [Define to 1 if have struct xt_set_info_match_v4])], [], [#include ]) fi if test $USE_LIBIPSET = Yes; then dnl -- Debian Buster has ipset v6.38, Bullseye v7.10. dnl -- Ubuntu 16.04 has ipset v6.29. dnl -- All other distros appear to have moved to ipset v7. AC_MSG_CHECKING([for libipset version 7 or later]) AC_COMPILE_IFELSE([AC_LANG_SOURCE( [[ #include void test_func(void) { ipset_session_init(NULL, NULL); } ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([LIBIPSET_PRE_V7_COMPAT], [ 1 ], [Define to 1 if libipset library version prior to v7]) add_system_opt[LIBIPSET_PRE_V7] ]) fi ]) LIBS="$SAV_LIBS" fi CPPFLAGS="$SAV_CPPFLAGS" fi if test $USE_IPTABLES = Yes; then AC_DEFINE([_WITH_IPTABLES_], [ 1 ], [Define to 1 if want iptables support]) add_system_opt([IPTABLES]) fi ]) AM_CONDITIONAL([LIBIPSET], [test $USE_LIBIPSET = Yes]) AM_CONDITIONAL([IPTABLES], [test $USE_IPTABLES = Yes]) AM_CONDITIONAL([LIBIPTC_DYNAMIC], [test $USE_IPTABLES = Yes -a .$LIBIPTC_DYNAMIC = .Yes]) AM_CONDITIONAL([LIBIPSET_DYNAMIC], [test $USE_LIBIPSET = Yes -a .${enable_libipset_dynamic} != .no]) unset LIBS dnl ----[Check for nftables libraries]---- USE_NFTABLES=No if test .${enable_nftables} != .no; then USE_NFTABLES=Yes dnl -- linux/netfilter/nf_tables.h since Linux 3.13 SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" AC_CHECK_DECL([NFTA_TABLE_MAX], [], [ AS_IF([test .${enable_nftables} = .yes], [AC_MSG_ERROR([nftables header files missing/not useable])]) USE_NFTABLES=No ], [#include ]) if test $USE_NFTABLES = Yes; then $PKG_CONFIG --exists libnftnl if test $? -ne 0; then USE_NFTABLES=No AC_MSG_WARN([libnftnl missing]) fi $PKG_CONFIG --exists libmnl if test $? -ne 0; then USE_NFTABLES=No AC_MSG_WARN([libmnl missing]) fi if test $USE_NFTABLES = Yes; then # nft prior to version 0.8.3 does not support type ifname in sets. We can't check the version of # nft, but we can check the version of libnftnl. nft v0.8.3 required libnftnl v1.0.9, but so did # nft v0.8.2. So play safe, and require the next version. LIBNFTNL_VERSION=`printf "0x%2.2x%2.2x%2.2xU" \`$PKG_CONFIG --modversion libnftnl | sed -e "s/\./ /g"\`` AC_DEFINE_UNQUOTED([LIBNFTNL_VERSION], [ $LIBNFTNL_VERSION ], [libnftnl version in hex]) add_pkg_config([libnftnl]) add_pkg_config([libmnl]) AC_DEFINE([_WITH_NFTABLES_], [ 1 ], [Define to 1 if want nftables support]) add_config_opt([NFTABLES]) AC_MSG_CHECKING([whether NFTNL_EXPR_LOOKUP_FLAGS and NFT_LOOKUP_F_INV are defined]) AC_LINK_IFELSE([AC_LANG_SOURCE([[ #include // libnftnl/expr.h requires this #include #include int main(void) { int i = NFTNL_EXPR_LOOKUP_FLAGS | NFT_LOOKUP_F_INV; return 0; } ]])], [ AC_MSG_RESULT(yes) AC_DEFINE([HAVE_NFTNL_EXPR_LOOKUP_FLAG_INV], [ 1 ], [Define to 1 if NFTNL_EXPR_LOOKUP_FLAGS and NFT_LOOKUP_F_INV defined]) ],[ AC_MSG_RESULT(no) ]) # nft l4proto from Linux 3.14 AC_CHECK_DECLS([NFT_META_L4PROTO], [], [], [#include ]) # nft dup from Linux 4.3 AC_CHECK_DECLS([NFTA_DUP_MAX], [], [], [#include ]) # nft meta oifkind from Linux 5.1 AC_CHECK_DECLS([NFT_META_OIFKIND], [], [], [#include ]) # NFT_USERDATA_MAXLEN since Linux 3.15. Check nftnl_udata_buf_alloc for libnftnl support of userdata USE_NFT_USERDATA=Yes AC_CHECK_DECLS([NFT_USERDATA_MAXLEN, nftnl_udata_buf_alloc], [], [USE_NFT_USERDATA=No], [ #include #include ] ) AS_IF([test $USE_NFT_USERDATA = Yes], [ AC_DEFINE([HAVE_NFTNL_UDATA], [ 1 ], [Define to 1 if have nftnl udata support]) AC_CHECK_DECLS([nftnl_udata_put_u32], [], [], [ #include #include ]) ]) # NFTNL_SET_DESC_CONCAT since Linux 5.6 and libnftnl 1.1.6, nft 0.9.4 - ranges in concatenations # NFTNL_SET_ELEM_KEY_END since Linux 5.6 and libnftnl 1.1.6, nft 0.9.4 # NFT_SET_CONCAT since Linux 5.7 NFT_RANGE_CONCATS_OK=1 AC_CHECK_DECLS([NFTNL_SET_DESC_CONCAT, NFTNL_SET_ELEM_KEY_END, NFT_SET_CONCAT], [], [NFT_RANGE_CONCATS_OK=0], [ #include #include ]) AS_IF([test $NFT_RANGE_CONCATS_OK -eq 1], [AC_DEFINE([NFT_RANGE_CONCATS], [ 1 ], [Define to 1 if can use concats with ranges in nftables])]) # NFTNL_SET_EXPR since Linux 5.7, libnftnl 1.1.7, nft 0.9.5 - counters on set elements AC_CHECK_DECLS([NFTNL_SET_EXPR], [], [], [[#include ]]) fi fi CPPFLAGS="$SAV_CPPFLAGS" fi AM_CONDITIONAL([NFTABLES], [test $USE_NFTABLES = Yes]) unset LIBS AS_IF([test $USE_IPTABLES = Yes -o $USE_NFTABLES = Yes], [AC_DEFINE([_WITH_FIREWALL_], [ 1 ], [Define to 1 if using iptables or nftables])]) AM_CONDITIONAL([FIREWALL], [test $USE_IPTABLES = Yes -o $USE_NFTABLES = Yes]) dnl ----[Check if have linux/if.h and net/if.h namespace collision]---- # Including and can cause a namespace collision. # Later versions of the headers are OK if linux/if.h is included second AC_MSG_CHECKING([for linux/if.h and net/if.h namespace collision]) SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_NET_LINUX_IF_H_COLLISION_], [ 1 ], [Define to 1 if have linux/if.h followed by net/if.h namespace collision]) add_system_opt([NET_LINUX_IF_H_COLLISION]) ]) CPPFLAGS="$SAV_CPPFLAGS" dnl ----[Check if linux/if_ether.h then netinet/in.h then linux/if.h namespace collision]---- # This issue was resolved in Linux 4.15.7/4.16 AC_MSG_CHECKING([for linux/if_ether.h then netinet/in.h then linux/if.h namespace collision]) SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include #include ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_LINUX_IF_ETHER_H_COLLISION_], [ 1 ], [Define to 1 if have linux/if_ether.h then netinet/in.h then linux/in.h namespace collision]) add_system_opt([NET_LINUX_IF_ETHER_H_COLLISION]) ]) CPPFLAGS="$SAV_CPPFLAGS" dnl ----[Check if have linux/if_ether.h and netinet/if_ether.h namespace collision]---- # Including and causes a namespace collision # with musl libc, but the collision only occurs if linux/ip_ether.h is included # before netinet/if_ether.h. The problem is that we want to include them in that # order. AC_MSG_CHECKING([for linux/if_ether.h then netinet/if_ether.h namespace collision]) SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_NETINET_LINUX_IF_ETHER_H_COLLISION_], [ 1 ], [Define to 1 if have linux/if_ether.h then netinet/if_ether.h namespace collision]) add_system_opt([NETINET_LINUX_IF_ETHER_H_COLLISION]) ]) CPPFLAGS="$SAV_CPPFLAGS" # Linux 4.5 to 4.5.4 has indirectly including # and which causes a namespace collision. AC_MSG_CHECKING([for libiptc/libiptc.h linux/if.h and net/if.h namespace collision]) SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([_HAVE_LIBIPTC_LINUX_NET_IF_H_COLLISION_], [ 1 ], [Define to 1 if have libiptc/libiptc.h linux/if.h and net/if.h namespace collision]) add_system_opt([LIBIPTC_LINUX_NET_IF_H_COLLISION]) ]) CPPFLAGS="$SAV_CPPFLAGS" dnl ----[ Checks for LVS, VRRP and BFD support ]---- IPVS_SYNCD_ATTRIBUTES=No IPVS_64BIT_STATS=No WITH_REGEX=No ENABLE_REGEX_DEBUG=No if test "$enable_lvs" != no; then IPVS_SUPPORT=Yes add_config_opt([LVS]) AC_DEFINE([_WITH_LVS_], [ 1 ], [Define to 1 if have IPVS support]) if test $IPVS_USE_NL = Yes; then AC_DEFINE([LIBIPVS_USE_NL], [ 1 ], [Define to 1 if libipvs can use netlink]) add_system_opt([LIBIPVS_NETLINK]) fi dnl ----[ IPVS syncd options ]--- SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" dnl -- Since Linux 3.18 AC_CHECK_DECLS([IPVS_DEST_ATTR_ADDR_FAMILY], [add_system_opt([IPVS_DEST_ATTR_ADDR_FAMILY])], [], [#include ]) dnl -- Since Linux 4.3 IPVS_SYNCD_ATTRIBUTES=Yes AC_CHECK_DECLS([ IPVS_DAEMON_ATTR_SYNC_MAXLEN, IPVS_DAEMON_ATTR_MCAST_GROUP, IPVS_DAEMON_ATTR_MCAST_GROUP6, IPVS_DAEMON_ATTR_MCAST_PORT, IPVS_DAEMON_ATTR_MCAST_TTL], [], [ IPVS_SYNCD_ATTRIBUTES=No break ], [[#include ]]) if test $IPVS_SYNCD_ATTRIBUTES = Yes; then AC_DEFINE([_HAVE_IPVS_SYNCD_ATTRIBUTES_], [ 1 ], [Define to 1 if have IPVS syncd attributes]) add_system_opt([IPVS_SYNCD_ATTRIBUTES]) fi dnl ----[ IPVS 64-bit stats ]---- dnl -- Since Linux 4.1 if test "$enable_lvs_64bit_stats" != "no"; then IPVS_64BIT_STATS=Yes AC_CHECK_DECLS([ IPVS_SVC_ATTR_STATS64, IPVS_DEST_ATTR_STATS64], [], [ IPVS_64BIT_STATS=No break ], [[#include ]]) if test $IPVS_64BIT_STATS = Yes; then AC_DEFINE([_WITH_LVS_64BIT_STATS_], [ 1 ], [Define to 1 if have IPVS 64 bit stats]) add_system_opt([IPVS_64BIT_STATS]) fi fi dnl ----[ IPVS tunnel type ]---- dnl -- Since Linux 5.2 AC_CHECK_DECLS([IPVS_DEST_ATTR_TUN_TYPE], [ AC_DEFINE([_HAVE_IPVS_TUN_TYPE_], [ 1 ], [Define to 1 if have IPVS tunnel type]) add_system_opt([IPVS_TUN_TYPE]) ], [], [[#include ]]) dnl -- Since Linux 5.3 AC_CHECK_DECLS([IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM], [ AC_DEFINE([_HAVE_IPVS_TUN_CSUM_], [ 1 ], [Define to 1 if have IPVS tunnel checksum options]) add_system_opt([IPVS_TUN_CSUM]) ], [], [[#include ]]) dnl -- Since Linux 5.3 AC_CHECK_DECLS([IP_VS_CONN_F_TUNNEL_TYPE_GRE], [ AC_DEFINE([_HAVE_IPVS_TUN_GRE_], [ 1 ], [Define to 1 if have IPVS gre tunnel]) add_system_opt([IPVS_TUN_GRE]) ], [], [[#include ]]) CPPFLAGS="$SAV_CPPFLAGS" dnl ----[ Is HTTP_GET regex checking wanted? ]---- AS_IF([test "$enable_regex" = yes], [ dnl -- Check pcre library has 8-bit support $PKG_CONFIG --exists libpcre2-8 HAVE_PCRE2=$? AS_IF([test $HAVE_PCRE2 -ne 0], [AC_MSG_ERROR([cannot find 8-bit pcre library])]) AC_MSG_CHECKING([for pcre.h]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #define PCRE2_CODE_UNIT_WIDTH 8 #include ]])], [ AC_MSG_RESULT([yes]) WITH_REGEX=Yes add_pkg_config([libpcre2-8]) AC_DEFINE([_WITH_REGEX_CHECK_], [ 1 ], [Define to 1 to build with HTTP_GET regex checking]) add_config_opt([REGEX]) ], [ AC_MSG_RESULT([no]) AC_MSG_ERROR([pcre2.h is missing]) ]) if test "$enable_regex_timers" = yes; then AC_DEFINE([_WITH_REGEX_TIMERS_], [ 1 ], [Define to 1 to include regex timers]) fi if test "${enable_regex_debug}" = yes; then AC_DEFINE([_REGEX_DEBUG_], [ 1 ], [Define to 1 to build with regex debugging support]) ENABLE_REGEX_DEBUG=Yes add_config_opt([REGEX_DEBUG]) fi ]) else IPVS_SUPPORT=No fi AM_CONDITIONAL([WITH_IPVS], [test $IPVS_SUPPORT = Yes]) AM_CONDITIONAL([WITH_REGEX], [test $WITH_REGEX = Yes]) dnl ----[ Checks for kernel netlink support ]---- VRRP_SUPPORT=No VRRP_AUTH_SUPPORT=No MACVLAN_SUPPORT=No ENABLE_JSON=No BFD_SUPPORT=No HAVE_CN_PROC=No WITH_TRACK_PROCESS=No if test "$enable_vrrp" != no; then VRRP_SUPPORT=Yes AC_DEFINE([_WITH_VRRP_], [ 1 ], [Define to 1 if have VRRP support]) add_config_opt([VRRP]) dnl ----[ check for VRRP authentication support ]---- if test "${enable_vrrp_auth}" != no; then VRRP_AUTH_SUPPORT=Yes AC_DEFINE([_WITH_VRRP_AUTH_], [ 1 ], [Define to 1 if want ARRP authentication support]) add_config_opt([VRRP_AUTH]) fi dnl ----[ Checks for kernel VMAC support ]---- SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" MACVLAN_SUPPORT=No if test "${enable_vmac}" != no; then MACVLAN_SUPPORT=Yes AC_DEFINE([_HAVE_VRRP_VMAC_], [ 1 ], [Define to 1 if have MAC VLAN support]) add_config_opt([VRRP_VMAC]) dnl ----[ Checks for kernel IPVLAN support ]---- IPVLAN_SUPPORT=Yes dnl -- Since Linux 3.19 AC_CHECK_DECLS([IFLA_IPVLAN_MODE], [], [ IPVLAN_SUPPORT=No break ], [[ #include #include ]]) if test $IPVLAN_SUPPORT = Yes; then dnl - IPVLAN_MODE_L3S since Linux 4.9, IFLA_IPVLAN_FLAGS since Linux 4.15 AC_CHECK_DECLS([IPVLAN_MODE_L3S, IFLA_IPVLAN_FLAGS], [], [], [[ #include ]]) AC_DEFINE([_HAVE_VRRP_IPVLAN_], [ 1 ], [Define to 1 if have IP VLAN support]) add_system_opt([VRRP_IPVLAN]) fi dnl ----[ Check for IFLA_LINK_NETNSID support ]---- since Linux v4.0 AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include int main(void) { int var = IFLA_LINK_NETNSID; } ]])], [ AC_DEFINE([HAVE_IFLA_LINK_NETNSID], [ 1 ], [Define to 1 if IFLA_LINK_NETNSID supported]) add_system_opt([IFLA_LINK_NETNSID]) ]) fi CPPFLAGS="$SAV_CPPFLAGS" dnl ----[ JSON output or not ? ]---- if test "${enable_json}" = yes; then ENABLE_JSON=Yes AC_DEFINE([_WITH_JSON_], [ 1 ], [Define to 1 to build with JSON output support]) add_config_opt([JSON]) fi dnl ----[ BFD support ? ]---- if test "${enable_bfd}" = yes; then BFD_SUPPORT=Yes AC_DEFINE([_WITH_BFD_], [ 1 ], [Define to 1 if have BFD support]) add_config_opt([BFD]) fi AS_IF([test .$enable_track_process != .no], [ WITH_TRACK_PROCESS=Yes AC_DEFINE([_WITH_TRACK_PROCESS_], [ 1 ], [Define to 1 if track-process not disabled]) ], [add_config_opt([DISABLE_TRACK_PROCESS])]) fi AM_CONDITIONAL([WITH_VRRP], [test $VRRP_SUPPORT = Yes]) AM_CONDITIONAL([VRRP_AUTH], [test $VRRP_AUTH_SUPPORT = Yes]) AM_CONDITIONAL([VMAC], [test $MACVLAN_SUPPORT = Yes]) AM_CONDITIONAL([WITH_JSON], [test $ENABLE_JSON = Yes]) AM_CONDITIONAL([WITH_BFD], [test $BFD_SUPPORT = Yes]) AM_CONDITIONAL([TRACK_PROCESS], [test $WITH_TRACK_PROCESS = Yes]) if test ${IPVS_SUPPORT} = No -a ${VRRP_SUPPORT} = No; then AC_MSG_ERROR([keepalived MUST be compiled with at least one of LVS or VRRP framework]) fi dnl ----[ Check for GLOB_BRACE support ]---- AC_CHECK_DECLS([GLOB_BRACE], [add_system_opt([GLOB_BRACE])], [], [[#include ]]) dnl ----[ Check for GLOB_ALTDIRFUNC support ]---- AC_CHECK_DECLS([GLOB_ALTDIRFUNC], [add_system_opt([GLOB_ALTDIRFUNC])], [], [[#include ]]) dnl ----[ Check for timegm() support ]---- AC_MSG_CHECKING([for timegm()]) SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$KA_CPPFLAGS $kernel_inc" AC_LINK_IFELSE([AC_LANG_SOURCE([[ #include int main(void) { timegm(NULL); return 0; } ]])], [ AC_MSG_RESULT(yes) AC_DEFINE([HAVE_TIMEGM], [ 1 ], [Define to 1 if have timegm()]) ],[ AC_MSG_RESULT(no) ]) CPPFLAGS="$SAV_CPPFLAGS" dnl ----[ Do we want v1.3.6 and earlier VRRPv3 unicast checksum compatibility support ]---- UNICAST_CHKSUM_COMPAT_SUPPORT=No if test .$enable_checksum_compat != .no; then UNICAST_CHKSUM_COMPAT_SUPPORT=Yes AC_DEFINE([_WITH_UNICAST_CHKSUM_COMPAT_], [ 1 ], [Define to 1 to enable v1.3.6 and earlier VRRPv3 unicast checksum compatibility]) add_config_opt([OLD_CHKSUM_COMPAT]) fi dnl ----[ Check if linkbeat wanted ]---- AS_IF([test .$enable_linkbeat = .no], [ LINKBEAT_SUPPORT=No add_config_opt([NO_LINKBEAT]) ], [ LINKBEAT_SUPPORT=Yes AC_DEFINE([_WITH_LINKBEAT_], [ 1 ], [Define to 1 if have linkbeat support]) ]) dnl ----[ Check if using sockaddr_storage wanted ]---- AS_IF([test .$enable_sockaddr_storage = .yes], [ AC_DEFINE([USE_SOCKADDR_STORAGE], [ 1 ], [Define to 1 if use sockaddr_storage]) add_config_opt([SOCKADDR_STORAGE]) ]) dnl ----[ Checks for kernel IFLA_INET6_ADDR_GEN_MODE support ]---- SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" if test ${MACVLAN_SUPPORT} = Yes; then # Introduced in Linux 3.17 AC_CHECK_DECLS([IFLA_INET6_ADDR_GEN_MODE], [ add_system_opt([INET6_ADDR_GEN_MODE]) ], [], [[ #include ]]) fi CPPFLAGS="$SAV_CPPFLAGS" dnl ----[ Checks for kernel IFLA_VRF_... support ]---- if test ${MACVLAN_SUPPORT} = Yes; then SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernelinc" # Introduced in Linux 4.3 AC_CHECK_DECLS([IFLA_VRF_MAX], [ add_system_opt([VRF]) AC_DEFINE([_HAVE_VRF_], [ 1 ], [Define to 1 if have kernel VRF support]) ], [], [[ #include ]]) CPPFLAGS="$SAV_CPPFLAGS" fi LIBNM_SUPPORT=No AS_IF([test \( ${MACVLAN_SUPPORT} = Yes -a .$enable_nm = .yes \)], [ dnl ----[ Check if libnm available ]---- $PKG_CONFIG --exists libnm AS_IF([ test $? -eq 0 ], [ AC_DEFINE([_HAVE_LIBNM_], [ 1 ], [Define to 1 to use libnm]) add_pkg_config([libnm]) add_system_opt([LIBNM]) LIBNM_SUPPORT=Yes ], [ AC_MSG_ERROR([--enable-nm requires NetworkManager libnm development package to be installed]) ]) ]) AM_CONDITIONAL([NETWORK_MANAGER], [test $LIBNM_SUPPORT = Yes]) dnl ----[ Checks for SNMP support ]---- SNMP_SUPPORT=No SNMP_KEEPALIVED_SUPPORT=No SNMP_VRRP_SUPPORT=No SNMP_RFC_SUPPORT=No SNMP_RFCV2_SUPPORT=No SNMP_RFCV3_SUPPORT=No SNMP_CHECKER_SUPPORT=No SNMP_V3_FOR_V2=No if test "$enable_snmp_keepalived" = yes; then AC_MSG_WARN([--enable-snmp-keepalived is obsolete. Use --enable-snmp-vrrp.]) enable_snmp_vrrp=$enable_snmp_keepalived fi if test "$enable_snmp" = yes -o \ "$enable_snmp_vrrp" = yes -o \ "$enable_snmp_checker" = yes -o \ "$enable_snmp_rfc" = yes -o \ "$enable_snmp_rfcv2" = yes -o \ "$enable_snmp_rfcv3" = yes; then AC_PATH_TOOL([NETSNMP_CONFIG], [net-snmp-config], [no]) if test "$NETSNMP_CONFIG" = no; then AC_MSG_ERROR([*** unable to find net-snmp-config]) fi # Despite the net-snmp-config documentation for the --*-libs and --base-cflags # options documention suggesting they provide the libraries and -I options # respectively, they actually include all the other # LDFLAGS and CFLAGS options # used when net-snmp was built. Since net-snmp is likely to have been built using # the distro's package builder, this can include quite a large number of # compiler/linker flags that we don't really want. # RedHat has a patch that stops the additional compile options being emitted with # --cflags and --base-cflags, however it doesn't have a patch to do similarly for # the --*-libs options. This means that the --cflags options do not include the # -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1, whereas the --*-libs options # do include -specs=/usr/lib/rpm/redhat/redhat-hardened-ld. The result of this is # -pie is specified for the linker, but -fpie is not specified for the compiler. # It turns out that this does not cause a problem on x86_64, but building on s390x # fails to link due to this mismatch. # See https://bugzilla.redhat.com/show_bug.cgi?id=1921988 for further details. NETSNMP_LIBS_AGENT=`${NETSNMP_CONFIG} --netsnmp-agent-libs` NETSNMP_LIBS_EXT=`${NETSNMP_CONFIG} --external-libs` NETSNMP_CFLAGS=`${NETSNMP_CONFIG} --base-cflags` NETSNMP_CPPFLAGS="-DNETSNMP_NO_INLINE" echo $NETSNMP_LIBS_EXT | $GREP -q -- -specs= AS_IF([ test $? -eq 0 ], [ HAVE_LD_SPECS=1 ], [ HAVE_LD_SPECS=0 ]) echo $NETSNMP_CFLAGS | $GREP -q -- -specs= AS_IF([ test $? -eq 0 ], [ HAVE_CC_SPECS=1 ], [ HAVE_CC_SPECS=0 ]) AS_IF([ test \( $HAVE_LD_SPECS -eq 1 -a $HAVE_CC_SPECS -eq 0 \)], [NETSNMP_LIBS_EXT=`echo $NETSNMP_LIBS_EXT | tr " " "\n" | grep "^-[lL]"`]) NETSNMP_LIBS="$NETSNMP_LIBS_AGENT $NETSNMP_LIBS_EXT" # net-snmp-config can add -I/usr/include, so remove it NETSNMP_CFLAGS=`echo $NETSNMP_CFLAGS " " | sed -e "s:-I */usr/include ::"` # net-snmp-config --base-cflags can specify include directories that don't # exist on the build system NEW_FLAGS= for f in `echo $NETSNMP_CFLAGS | tr " " "\n" | grep "^-I"`; do DIR=`echo $f | sed -e "s/^-I *//"` AS_IF([ test -d $DIR ], [ NEW_FLAGS+=" -I$DIR" ]) done NETSNMP_CFLAGS=$NEW_FLAGS # net-snmp-config can specify LTO flags; remove them so they are only # included if --enable-lto is specified NETSNMP_CFLAGS=`echo " $NETSNMP_CFLAGS " | sed -e "s/-f[[^ ]]*lto[[^ ]]* //g"` # net-snmp-config adds compiler and linker options that were set at the time # net-snmp was built, and this can include spec files that may not exist # on the system building keepalived. We need to check if any spec files # are specified, and if they do not exist on this system, then remove them # from NETSNMP_LIBS or NETSNMP_CFLAGS. # For further information, see https://bugzilla.redhat.com/show_bug.cgi?id=1544527 # and the other bugs referred to in it. for spec in `echo $NETSNMP_LIBS | sed -e "s? ?\n?g" | grep "^-specs="`; do SPEC_FILE=`echo $spec | sed -e "s?^-specs=??"` if test ! -f $SPEC_FILE; then NETSNMP_LIBS=`echo $NETSNMP_LIBS | sed -e "s? *$spec *? ?"` AC_MSG_WARN([Removing $spec from NETSNMP_LIBS since spec file not installed]) fi done for spec in `echo $NETSNMP_CFLAGS | sed -e "s? ?\n?g" | grep "^-specs="`; do SPEC_FILE=`echo $spec | sed -e "s?^-specs=??"` if test ! -f $SPEC_FILE; then NETSNMP_CFLAGS=`echo $NETSNMP_CFLAGS | sed -e "s? *$spec *? ?"` AC_MSG_WARN([Removing $spec from NETSNMP_CFLAGS since spec file not installed]) fi done SAV_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS ${NETSNMP_CFLAGS}" SAV_LIBS="$LIBS" LIBS="$LIBS ${NETSNMP_LIBS}" AC_MSG_CHECKING([whether C compiler supports flag "${NETSNMP_CFLAGS} ${NETSNMP_LIBS}" from Net-SNMP]) AC_LINK_IFELSE([AC_LANG_SOURCE([[ int main(void) { return 0; } ]])], [ AC_MSG_RESULT(yes) ],[ AC_MSG_RESULT(no) AC_MSG_ERROR([*** incorrect CFLAGS from net-snmp-config]) ]) # Do we have subagent support? AC_CHECK_FUNCS([netsnmp_enable_subagent], [], [AC_MSG_ERROR([*** no subagent support in net-snmp])]) # check for net-snmp headers # Some ancient distributions may miss header SAV_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $kernel_inc $NETSNMP_CFLAGS" AC_CHECK_HEADERS(net-snmp/agent/agent_sysORTable.h net-snmp/agent/snmp_vars.h net-snmp/agent/util_funcs.h,[], [AC_MSG_ERROR([missing net-snmp headers])],[[ #include #include #include ]]) SNMP_SUPPORT=Yes # NETSNMP_CFLAGS can have CPPFLAGS options, so separate them NETSNMP_CPPFLAGS_XTRA=`echo " $NETSNMP_CFLAGS " | sed -e "s/ / /g" -e "s/ -[[^IDU]] *-/ -/g" -e "s/ -[[^IDU]] *[[^-]][[^ ]]* / /g" -e "s/ */ /g"` NETSNMP_CFLAGS=`echo " $NETSNMP_CFLAGS " | sed -e "s/ / /g" -e "s/ -[[IDU]] *[[^ ]]* / /g" -e "s/ */ /g"` add_to_var([KA_CFLAGS], [$NETSNMP_CFLAGS]) add_to_var([KA_CPPFLAGS], [$NETSNMP_CPPFLAGS $NETSNMP_CPPFLAGS_XTRA]) # NETSNMP_LIBS may have some LDFLAGS options, so separate them NETSNMP_LDFLAGS_XTRA=`echo " $NETSNMP_LIBS " | sed -e "s/ / /g" -e "s/ -l *[[^ ]]* / /g" -e "s/ */ /g" -e "s/ -/ @-/g" | tr "@" "\n" | sed -e "s/^ *//" -e "s/ *$//" | sort -u | tr "\n" " "` NETSNMP_LIBS=`echo " $NETSNMP_LIBS " | sed -e "s/ / /g" -e "s/ \(-l *[[^ ]]*\) /@\1@/g" | tr "@" "\n" | grep "^-l" | tr "\n" " " | sed -e "s/ */ /g"` add_to_var([KA_LDFLAGS], [$NETSNMP_LDFLAGS $NETSNMP_LDFLAGS_XTRA]) add_to_var([KA_LIBS], [$NETSNMP_LIBS]) if test "$enable_snmp_rfc" = yes; then SNMP_RFCV2_SUPPORT=Yes SNMP_RFCV3_SUPPORT=Yes else if test "$enable_snmp_rfcv2" = yes; then SNMP_RFCV2_SUPPORT=Yes fi if test "$enable_snmp_rfcv3" = yes; then SNMP_RFCV3_SUPPORT=Yes fi fi if test ${SNMP_RFCV2_SUPPORT} = Yes -o \ ${SNMP_RFCV3_SUPPORT} = Yes; then if test ${VRRP_SUPPORT} != Yes; then AC_MSG_ERROR([RFC SNMP support requires VRRP]) fi SNMP_RFC_SUPPORT=Yes fi if test ${SNMP_RFCV3_SUPPORT} = Yes -a \ "$enable_snmp_reply_v3_for_v2" != no; then AC_DEFINE([_SNMP_REPLY_V3_FOR_V2_], [ 1 ], [Define to 1 to have keepalived send RFC6527 SNMP responses for VRRPv2 instances]) SNMP_V3_FOR_V2=Yes add_config_opt([SNMP_V3_FOR_V2]) fi if test "$enable_snmp" = yes; then if test ${VRRP_SUPPORT} = Yes; then SNMP_VRRP_SUPPORT=Yes fi if test ${IPVS_SUPPORT} = Yes; then SNMP_CHECKER_SUPPORT=Yes fi else if test "$enable_snmp_vrrp" = yes; then SNMP_VRRP_SUPPORT=Yes fi if test "$enable_snmp_checker" = yes; then SNMP_CHECKER_SUPPORT=Yes fi fi if test ${VRRP_SUPPORT} != Yes -a \ ${SNMP_VRRP_SUPPORT} = Yes; then AC_MSG_ERROR([VRRP SNMP support requires VRRP]) fi if test ${IPVS_SUPPORT} = No -a \ ${SNMP_CHECKER_SUPPORT} = Yes; then AC_MSG_ERROR([CHECKER SNMP support requires checker]) fi if test ${SNMP_VRRP_SUPPORT} = Yes -o \ ${SNMP_CHECKER_SUPPORT} = Yes; then SNMP_KEEPALIVED_SUPPORT=Yes fi CPPFLAGS="$SAV_CPPFLAGS" CFLAGS="$SAV_CFLAGS" LIBS="$SAV_LIBS" fi dnl ----[What SNMP support is required]---- if test $SNMP_SUPPORT = Yes; then AC_DEFINE([_WITH_SNMP_], [ 1 ], [Define to 1 to have SNMP support]) fi if test $SNMP_KEEPALIVED_SUPPORT = Yes; then AC_DEFINE([_WITH_SNMP_KEEPALIVED_], [ 1 ], [Define to 1 to have keepalived SNMP support]) fi if test $SNMP_VRRP_SUPPORT = Yes; then AC_DEFINE([_WITH_SNMP_VRRP_], [ 1 ], [Define to 1 to have keepalived SNMP VRRP support]) add_config_opt([SNMP_VRRP]) fi if test $SNMP_CHECKER_SUPPORT = Yes; then AC_DEFINE([_WITH_SNMP_CHECKER_], [ 1 ], [Define to 1 to have keepalived SNMP checker support]) add_config_opt([SNMP_CHECKER]) fi if test $SNMP_RFC_SUPPORT = Yes; then AC_DEFINE([_WITH_SNMP_RFC_], [ 1 ], [Define to 1 to have RFC SNMP support]) fi if test $SNMP_RFCV2_SUPPORT = Yes; then AC_DEFINE([_WITH_SNMP_RFCV2_], [ 1 ], [Define to 1 to have RFCv2 SNMP support]) add_config_opt([SNMP_RFCV2]) fi if test $SNMP_RFCV3_SUPPORT = Yes; then AC_DEFINE([_WITH_SNMP_RFCV3_], [ 1 ], [Define to 1 to have RFCv3 SNMP support]) add_config_opt([SNMP_RFCV3]) fi AM_CONDITIONAL([SNMP], [test $SNMP_SUPPORT = Yes]) AM_CONDITIONAL([SNMP_KEEPALIVED], [test $SNMP_KEEPALIVED_SUPPORT = Yes]) AM_CONDITIONAL([SNMP_VRRP], [test $SNMP_VRRP_SUPPORT = Yes -o $SNMP_RFC_SUPPORT = Yes]) AM_CONDITIONAL([SNMP_CHECKER], [test $SNMP_CHECKER_SUPPORT = Yes]) AM_CONDITIONAL([SNMP_RFC], [test $SNMP_RFCV2_SUPPORT = Yes -o $SNMP_RFCV3_SUPPORT = Yes]) AM_CONDITIONAL([SNMP_RFCV2], [test $SNMP_RFCV2_SUPPORT = Yes]) AM_CONDITIONAL([SNMP_RFCV3], [test $SNMP_RFCV3_SUPPORT = Yes]) AM_CONDITIONAL([SNMP_REPLY_V3_FOR_V2], [test $SNMP_V3_FOR_V2 = Yes]) AS_IF([test $SNMP_SUPPORT = Yes], [SNMP_SERVICE=snmpd.service], [SNMP_SERVICE=]) AC_SUBST([SNMP_SERVICE]) dnl ----[ Check for Dbus support ]---- DBUS_SUPPORT=No DBUS_CREATE_INSTANCE=No if test "$enable_dbus" = yes; then AC_CHECK_LIB(gio-2.0, g_bus_own_name, [ add_pkg_config([gio-2.0]) DBUS_SUPPORT=Yes AC_DEFINE([_WITH_DBUS_], [ 1 ], [Define to 1 to have DBUS support]) add_config_opt([DBUS]) dnl -- g_type_init() not needed and deprecated since glib 2.36 SAV_CFLAGS=$CFLAGS CFLAGS=`$PKG_CONFIG --cflags gio-2.0` SAV_LIBS=$LIBS LIBS=`$PKG_CONFIG --libs gio-2.0` AC_RUN_IFELSE( [ AC_LANG_PROGRAM( [[#include ]], [[return !g_thread_functions_for_glib_use.mutex_lock;]])], [need_g_type_init=0], [need_g_type_init=1], [ AC_MSG_WARN([Cannot determine if need to call g_type_init(). Assuming yes for safety.]) need_g_type_init=1 ]) if test $need_g_type_init -eq 1; then AC_DEFINE([DBUS_NEED_G_TYPE_INIT], [ 1 ], [Define to 1 if need to call g_type_init()]) fi LIBS=$SAV_LIBS CFLAGS=$SAV_CFLAGS if test "$enable_dbus_create_instance" = yes; then AC_DEFINE([_WITH_DBUS_CREATE_INSTANCE_], [ 1 ], [Define to 1 to have DBus create instance support]) DBUS_CREATE_INSTANCE=Yes add_config_opt([DBUS_CREATE_INSTANCE]) AC_MSG_WARN([DBus create instance functionality is dangerous - why do you want it?]) fi ], [AC_MSG_ERROR([DBUS support requested but libgio-2.0 not found.])]) unset LIBS fi AM_CONDITIONAL([WITH_DBUS], [test $DBUS_SUPPORT = Yes]) AM_CONDITIONAL([DBUS_CREATE_INSTANCE], [test $DBUS_CREATE_INSTANCE = Yes]) dnl ----[ check for SO_MARK support ]---- dnl -- Since Linux 2.6.25 SO_MARK_SUPPORT=No if test "${enable_fwmark}" != no; then AC_CHECK_DECLS([SO_MARK], [ SO_MARK_SUPPORT=Yes AC_DEFINE([_WITH_SO_MARK_], [ 1 ], [Define to 1 if have SO_MARK]) add_system_opt([SO_MARK]) ], [], [[#include ]]) fi dnl -- Do we want GNU standard paths (moves .pid files) GNU_STD_PATHS=No if test "${enable_gnu_std_paths}" = "yes"; then AC_DEFINE([GNU_STD_PATHS], [ 1 ], [set to enforce GNU standard paths, for .pid files etc]) fi if test $TMP_DIR_SPECIFIED = Y; then AS_IF([test `expr substr ${with_tmp_dir} 1 1` != /], [AC_MSG_ERROR([tmp-dir must be absolute path])]) # Remove any trailing / - someone will include it sometime KA_TMP_DIR=`echo ${with_tmp_dir} | sed -e "s:/*$::"` else KA_TMP_DIR=/tmp 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 $(echo $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 echo $i | grep -q /etc AS_IF([test $? -eq 0], [iproute_etc_dir=$i]) echo $i | grep -q /usr 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]) ]) 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" CFLAGS="-Wformat -Werror=format $SAV_CPPFLAGS $KA_CPPFLAGS" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include int main(int argc, char **argv) { rlim_t val = 23U; printf("%lu %d %p", val, argc, argv); return 0; } ]])], [AC_DEFINE([PRI_rlim_t], ["lu"], [Define printf format specifier for rlim_t])], [AC_DEFINE([PRI_rlim_t], ["llu"], [Define printf format specifier for rlim_t])], ) CFLAGS="$SAV_CFLAGS" dnl ---[ Do we need __FAVOR_BSD for tcp.h and udp.h header files to be usable? ]--- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include int main(int argc, char **argv) { struct tcphdr hdr; hdr.th_dport = argc; } ]])], [], [AC_DEFINE([NEED_FAVOR_BSD], [ 1 ], [Define if using VERY old (i.e. pre 2.19 - 2014) glibc])], ) dnl ---[ check for sphinx-build executable ]---- if test -z "$SPHINXBUILD"; then SPHINXBUILDNAME=sphinx-build else SPHINXBUILDNAME=${SPHINXBUILD} fi AC_SUBST(SPHINXBUILDNAME) AC_CHECK_PROG([HAVE_SPHINX_BUILD], [$SPHINXBUILDNAME], [Yes], [No]) AM_CONDITIONAL([BUILD_DOCS], [test $HAVE_SPHINX_BUILD = Yes]) dnl ----[ Memory alloc check or not ? ]---- MEM_CHECK=No MEM_CHECK_LOG=No if test "${enable_mem_check}" = "yes"; then MEM_CHECK=Yes AC_DEFINE([_MEM_CHECK_], [ 1 ], [Define to 1 to build with malloc/free checks]) add_config_opt([MEM_CHECK]) if test "${enable_mem_check_log}" = "yes"; then MEM_CHECK_LOG=Yes AC_DEFINE([_MEM_CHECK_LOG_], [ 1 ], [Define to 1 to log malloc/free checks to syslog]) add_config_opt([MEM_CHECK_LOG]) fi fi dnl ----[ Malloc check or not ? ]---- MALLOC_CHECK=No AS_IF([test ."${enable_malloc_check}" = ."yes"], [ MALLOC_CHECK=Yes AC_DEFINE([_MALLOC_CHECK_], [ 1 ], [Define to 1 to build with malloc failure checks]) add_config_opt([MALLOC_CHECK]) ]) dnl ----[ OpenSSL memory alloc check or not ? ]---- OPENSSL_MEM_CHECK=No if test "${enable_openssl_mem_check}" = "yes"; then OPENSSL_MEM_CHECK=Yes AC_DEFINE([_OPENSSL_MEM_CHECK_], [ 1 ], [Define to 1 to build with malloc/free checks]) add_config_opt([OPENSSL_MEM_CHECK]) fi dnl ----[ Log calls to set_time or not ? ]---- TIMER_CHECK=No if test "${enable_timer_check}" = "yes"; then TIMER_CHECK=Yes AC_DEFINE([_TIMER_CHECK_], [ 1 ], [Define to 1 to build with set time logging]) add_config_opt([TIMER_CHECK]) fi AS_IF([test .$enable_vrrp != .no], [ FAULT_FLAGS_CHECK=No AS_IF([test .${enable_fault_flags_check} = .yes], [ FAULT_FLAGS_CHECK=Yes AC_DEFINE([_FAULT_FLAGS_CHECK_], [ 1 ], [Define to 1 to build with vrrp fault flag checking]) add_config_opt([FAULT_FLAGS_CHECK]) ]) ]) dnl ----[ Debug in one process or not ? ]---- if test "${enable_one_process_debug}" = yes; then AC_DEFINE([_ONE_PROCESS_DEBUG_], [ 1 ], [Define to 1 to build with debugging support]) ENABLE_ONE_PROCESS_DEBUG=Yes add_config_opt([ONE_PROCESS_DEBUG]) else ENABLE_ONE_PROCESS_DEBUG=No fi AM_CONDITIONAL([ONE_PROCESS_DEBUG], [test $ENABLE_ONE_PROCESS_DEBUG = Yes]) dnl ----[ Netlink command timers or not ? ]---- if test "${enable_netlink_timers}" = yes; then AC_DEFINE([_NETLINK_TIMERS_], [ 1 ], [Define to 1 to build with netlink command timers support]) ENABLE_NETLINK_TIMERS=Yes add_config_opt([NETLINK_TIMERS]) else ENABLE_NETLINK_TIMERS=No fi dnl ----[ smtp-alert debugging or not ? ]---- if test "${enable_smtp_alert_debug}" = yes; then AC_DEFINE([_SMTP_ALERT_DEBUG_], [ 1 ], [Define to 1 to build with smtp-alert debugging support]) ENABLE_SMTP_ALERT_DEBUG=Yes add_config_opt([SMTP_ALERT_DEBUG]) ENABLE_LOG_FILE_APPEND=Yes else ENABLE_SMTP_ALERT_DEBUG=No fi dnl ----[ Stacktrace support or not ? ]---- if test "${enable_stacktrace}" = yes; then AC_DEFINE([_WITH_STACKTRACE_], [ 1 ], [Define to 1 to build with stacktrace support]) ENABLE_STACKTRACE=Yes add_config_opt([STACKTRACE]) add_to_var([KA_LDFLAGS], [-rdynamic]) else ENABLE_STACKTRACE=No fi dnl ----[ Thread dumping support or not ? ]---- if test "${enable_dump_threads}" = yes; then AC_DEFINE([_WITH_DUMP_THREADS_], [ 1 ], [Define to 1 to build with thread dumping support]) ENABLE_DUMP_THREADS=Yes add_config_opt([DUMP_THREADS]) else ENABLE_DUMP_THREADS=No fi dnl ----[ epoll() debugging support or not ? ]---- if test "${enable_epoll_debug}" = yes; then AC_DEFINE([_EPOLL_DEBUG_], [ 1 ], [Define to 1 to build with epoll_wait() debugging support]) ENABLE_EPOLL_DEBUG=Yes add_config_opt([EPOLL_DEBUG]) else ENABLE_EPOLL_DEBUG=No fi dnl ----[ epoll() thread dumping support or not ? ]---- if test "${enable_epoll_thread_dump}" = yes; then AC_DEFINE([_EPOLL_THREAD_DUMP_], [ 1 ], [Define to 1 to build with epoll thread dumping support]) ENABLE_EPOLL_THREAD_DUMP=Yes add_config_opt([EPOLL_THREAD_DUMP]) else ENABLE_EPOLL_THREAD_DUMP=No fi if test $ENABLE_EPOLL_THREAD_DUMP = Yes -o $ENABLE_DUMP_THREADS = Yes -o $ENABLE_EPOLL_DEBUG = Yes; then AC_DEFINE([THREAD_DUMP], [ 1 ], [Define to 1 to build with thread dumping support]) fi dnl ----[ TSM debugging support or not ? ]---- if test "${enable_tsm_debug}" = yes; then AC_DEFINE([_TSM_DEBUG_], [ 1 ], [Define to 1 to build with TSM debugging support]) ENABLE_TSM_DEBUG=Yes add_config_opt([TSM_DEBUG]) else ENABLE_TSM_DEBUG=No fi dnl ----[ VRRP FD debugging support or not ? ]---- if test "${enable_vrrp_fd_debug}" = yes; then AC_DEFINE([_VRRP_FD_DEBUG_], [ 1 ], [Define to 1 to build with vrrp fd debugging support]) ENABLE_VRRP_FD_DEBUG=Yes add_config_opt([VRRP_FD_DEBUG]) else ENABLE_VRRP_FD_DEBUG=No fi dnl ----[ network timestamp support or not ? ]---- if test "${enable_network_timestamp}" = yes; then AC_DEFINE([_NETWORK_TIMESTAMP_], [ 1 ], [Define to 1 to build with network timestamp support]) ENABLE_NETWORK_TIMESTAMP=Yes add_config_opt([NETWORK_TIMESTAMP]) else ENABLE_NETWORK_TIMESTAMP=No fi dnl ----[ asserts enabled or not ? ]---- if test "${enable_asserts}" = yes; then AC_DEFINE([_ENABLE_ASSERT_], [ 1 ], [Define to 1 to enable asserts]) ENABLE_ASSERT=Yes add_config_opt([ASSERT]) else ENABLE_ASSERT=No fi AM_CONDITIONAL([ASSERTS], [test $ENABLE_ASSERT = Yes]) dnl ----[ Specify interface type to be unchangeable ]---- if test "${with_fixed_if_type}"; then if test "${with_fixed_if_type}" = yes -o ${with_fixed_if_type} = no; then AC_MSG_ERROR([An interface type must be specified with --with-fixed-if-type]) fi AC_DEFINE_UNQUOTED([_FIXED_IF_TYPE_], [ "${with_fixed_if_type}" ], [Consider ${with_fixed_if_type} interfaces to be unchangeable]) FIXED_IF_TYPE=${with_fixed_if_type} add_config_opt([FIXED_IF_TYPE=${with_fixed_if_type}]) else FIXED_IF_TYPE= fi dnl ----[ Profiling or not ? ]---- WITH_PROFILING=No if test "${enable_profile}" = yes; then # gprof keepalived/keepalived /gmon.keepalived.PID >/tmp/prof.PID.op to generate profile WITH_PROFILING=Yes add_config_opt([PROFILING]) add_to_var([KA_CFLAGS], [-pg]) AC_DEFINE([_WITH_PROFILING_], [ 1 ], [Define to 1 to build with profiling support]) fi AM_CONDITIONAL([PROFILE], [test $WITH_PROFILING = Yes]) dnl ----[ perf support or not? keepalived provides runtime options ]---- if test "${enable_perf}" = yes; then AC_DEFINE([_WITH_PERF_], [ 1 ], [Define to 1 to build with perf support]) ENABLE_PERF=Yes add_config_opt([PERF]) add_to_var([KA_CFLAGS], [-pg]) else ENABLE_PERF=No fi # consider -fsanitize-recover=all or -fsanitize-recover=address -fsanitize=pointer-compare -fsanitize=pointer-subtract (pointer-subtract needs option detect_invalid_pointer_pairs=2) SANITIZERS= dnl ----[ sanitize=address testing or not? ]---- AS_IF([test "${enable_sanitize_address}" = yes], [ AC_DEFINE([_WITH_SANITIZE_ADDRESS_], [ 1 ], [Define to 1 to build with sanitize=address support]) ENABLE_SANITIZE_ADDRESS=Yes add_config_opt([SANITIZE_ADDRESS]) SANITIZERS="$SANITIZERS,address" AS_IF([test "${enable_sanitize_address_options}" != no -a -n "${enable_sanitize_address_options}"], [AC_DEFINE_UNQUOTED([_ASAN_DEFAULT_OPTIONS_], ["${enable_sanitize_address_options}"], [default options for ASAN])]) ],[ ENABLE_SANITIZE_ADDRESS=No ] ) dnl ----[ sanitize=hwaddress testing or not? ]---- if test "${enable_sanitize_hwaddress}" = yes; then AC_DEFINE([_WITH_SANITIZE_HWADDRESS_], [ 1 ], [Define to 1 to build with sanitize=hwaddress support]) ENABLE_SANITIZE_HWADDRESS=Yes add_config_opt([SANITIZE_HWADDRESS]) SANITIZERS="$SANITIZERS,hwaddress" AS_IF([test "${enable_sanitize_hwaddress_options}" != no -a -n "${enable_sanitize_hwaddress_options}"], [AC_DEFINE_UNQUOTED([_HWASAN_DEFAULT_OPTIONS_], ["${enable_sanitize_hwaddress_options}"], [default options for HWASAN])]) else ENABLE_SANITIZE_HWADDRESS=No fi dnl ----[ sanitize=undefined testing or not? ]---- echo enable_Sanitize_undefined = $enable_sanitize_undefined if test "${enable_sanitize_undefined}" = yes; then AC_DEFINE([_WITH_SANITIZE_UNDEFINED_], [ 1 ], [Define to 1 to build with sanitize=undefined support]) ENABLE_SANITIZE_UNDEFINED=Yes add_config_opt([SANITIZE_UNDEFINED]) add_to_var([KA_CFLAGS], ["--param=max-inline-insns-single=100000"]) add_to_var([KA_CFLAGS], ["--param=large-function-growth=500"]) SANITIZERS="$SANITIZERS,undefined" AS_IF([test "${enable_sanitize_undefined_options}" != no -a -n "${enable_sanitize_undefined_options}"], [AC_DEFINE_UNQUOTED([_UBSAN_DEFAULT_OPTIONS_], ["${enable_sanitize_undefined_options}"], [default options for UBSAN])]) else ENABLE_SANITIZE_UNDEFINED=No fi dnl ----[ sanitize=memory testing or not? ]---- if test "${enable_sanitize_memory}" = yes; then AC_DEFINE([_WITH_SANITIZE_MEMORY_], [ 1 ], [Define to 1 to build with sanitize=memory support]) ENABLE_SANITIZE_MEMORY=Yes add_config_opt([SANITIZE_MEMORY]) SANITIZERS="$SANITIZERS,memory" AS_IF([test "${enable_sanitize_memory_options}" != no -a -n "${enable_sanitize_memory_options}"], [AC_DEFINE_UNQUOTED([_MSAN_DEFAULT_OPTIONS_], ["${enable_sanitize_memory_options}"], [default options for MSAN])]) else ENABLE_SANITIZE_MEMORY=No fi dnl ----[ sanitize=leak testing or not? ]---- if test "${enable_sanitize_leak}" = yes; then AC_DEFINE([_WITH_SANITIZE_LEAK_], [ 1 ], [Define to 1 to build with sanitize=leak support]) ENABLE_SANITIZE_LEAK=Yes add_config_opt([SANITIZE_LEAK]) SANITIZERS="$SANITIZERS,leak" AS_IF([test "${enable_sanitize_leak_options}" != no -a -n "${enable_sanitize_leak_options}"], [AC_DEFINE_UNQUOTED([_LSAN_DEFAULT_OPTIONS_], ["${enable_sanitize_leak_options}"], [default options for LSAN])]) else ENABLE_SANITIZE_LEAK=No fi dnl ----[ sanitize=scudo testing or not? ]---- if test "${enable_sanitize_scudo}" = yes; then AC_DEFINE([_WITH_SANITIZE_SCUDO_], [ 1 ], [Define to 1 to build with sanitize=scudo support]) ENABLE_SANITIZE_SCUDO=Yes add_config_opt([SANITIZE_SCUDO]) SANITIZERS="$SANITIZERS,scudo" AS_IF([test "${enable_sanitize_scudo_options}" != no -a -n "${enable_sanitize_scudo_options}"], [AC_DEFINE_UNQUOTED([_SCUDO_DEFAULT_OPTIONS_], ["${enable_sanitize_scudo_options}"], [default options for SCUDO])]) else ENABLE_SANITIZE_SCUDO=No fi AS_IF([test -n "$SANITIZERS"], [ add_to_var([KA_CFLAGS], ["-fsanitize=${SANITIZERS:1}"]) # Skip leading , add_to_var([KA_CFLAGS], ["-g"]) AC_DEFINE([_WITH_SANITIZER_], [ 1 ], [Define to 1 if any sanitizer is enabled]) ]) AM_CONDITIONAL([WITH_SANITIZER], [test -n "$SANITIZERS"]) if test "${enable_log_file}" = yes; then AC_DEFINE([ENABLE_LOG_TO_FILE], [ 1 ], [Define if enabling logging to files]) ENABLE_LOG_FILE_APPEND=Yes add_config_opt([FILE_LOGGING]) fi if test "${ENABLE_LOG_FILE_APPEND}" = Yes; then AC_DEFINE([ENABLE_LOG_FILE_APPEND], [ 1 ], [Define if appending to log files is allowed]) add_config_opt([LOG_FILE_APPEND]) fi dnl ----[ Do we want RECVMSG debugging code]---- ENABLE_RECVMSG_DEBUG=No AS_IF([test .$enable_recvmsg_debug = .yes], [ AC_DEFINE([_RECVMSG_DEBUG_], [ 1 ], [Define to add VRRP recvmsg() debugging code]) add_config_opt([RECVMSG_DEBUG]) ENABLE_RECVMSG_DEBUG=Yes ]) dnl ----[ Do we need to check for EINTR, or enable EINTR debugging code]---- ENABLE_EINTR_DEBUG=No AS_IF([test .$enable_eintr_debug = .yes], [ AC_DEFINE([_EINTR_DEBUG_], [ 1 ], [Define to test for and log errno == EINTR when no asynchronous signal handlers]) add_config_opt([EINTR_DEBUG]) ENABLE_EINTR_DEBUG=Yes ], [ AS_IF([test .$enable_eintr_debug = .check], [ AC_DEFINE([CHECK_EINTR], [ 1 ], [Define if need to check for EINTR errno]) add_config_opt([EINTR_CHECK]) ]) ]) dnl ----[ Do we enable script debugging code]---- ENABLE_SCRIPT_DEBUG=No AS_IF([test .$enable_script_debug = .yes], [ AC_DEFINE([_SCRIPT_DEBUG_], [ 1 ], [Define to enable script debugging support]) add_config_opt([SCRIPT_DEBUG]) ENABLE_SCRIPT_DEBUG=Yes ]) dnl ----[ Do we want to enable track process debugging code]---- ENABLE_TRACK_PROCESS_DEBUG=No AS_IF([test .$enable_track_process != .no], [ AS_IF([test .$enable_track_process_debug = .yes], [ AC_DEFINE([_TRACK_PROCESS_DEBUG_], [ 1 ], [Define to enable logging all process connector events]) add_config_opt([TRACK_PROCESS_DEBUG]) ENABLE_TRACK_PROCESS_DEBUG=Yes ]) ]) dnl ----[ Do we want to enable parser debugging code]---- ENABLE_PARSER_DEBUG=No AS_IF([test .$enable_parser_debug = .yes], [ AC_DEFINE([_PARSER_DEBUG_], [ 1 ], [Define to enable parser debugging]) add_config_opt([PARSER_DEBUG]) ENABLE_PARSER_DEBUG=Yes ]) dnl ----[ Do we want to enable checksum debugging code]---- ENABLE_CHECKSUM_DEBUG=No AS_IF([test .$enable_checksum_debug = .yes], [ AC_DEFINE([_CHECKSUM_DEBUG_], [ 1 ], [Define to enable checksum debugging]) add_config_opt([CHECKSUM_DEBUG]) ENABLE_CHECKSUM_DEBUG=Yes ]) dnl ----[ Do we want to enable checker debugging code]---- ENABLE_CHECKER_DEBUG=No AS_IF([test .$enable_checker_debug = .yes], [ AC_DEFINE([_CHECKER_DEBUG_], [ 1 ], [Define to enable checker debugging]) add_config_opt([CHECKER_DEBUG]) ENABLE_CHECKER_DEBUG=Yes ]) dnl ----[ Do we want to enable SMTP connect debugging code]---- ENABLE_SMTP_CONNECT_DEBUG=No AS_IF([test .$enable_smtp_connect_debug = .yes], [ AC_DEFINE([_SMTP_CONNECT_DEBUG_], [ 1 ], [Define to enable SMTP connection debugging]) add_config_opt([SMTP_CONNECT_DEBUG]) ENABLE_SMTP_CONNECT_DEBUG=Yes ]) dnl ----[ Do we want to enable memory alloc/free error debugging code]---- ENABLE_MEM_ERR_DEBUG=No AS_IF([test .$enable_mem_err_debug = .yes], [ AC_DEFINE([_MEM_ERR_DEBUG_], [ 1 ], [Define to enable memory alloc/free error debugging]) add_config_opt([MEM_ERR_DEBUG]) ENABLE_MEM_ERR_DEBUG=Yes ]) dnl ----[ Do we want to enable dump keywords code]---- AS_IF([test .$enable_dump_keywords = .yes], [ AC_DEFINE([_DUMP_KEYWORDS_], [ 1 ], [Define to enable keyword dumping]) add_config_opt([DUMP_KEYWORDS]) ]) if test "${NEED_LIBDL}" = Yes; then add_to_var([KA_LIBS], [-ldl]) fi dnl ----[ Determine if we are using pthreads ]---- echo " $KA_LIBS" | grep -qE -- " -l?pthread " if test $? -eq 0 ;then AC_DEFINE([_WITH_PTHREADS_], [ 1 ], [Define to 1 if using pthreads]) fi dnl ----[ Check if rpmbuild supports --build-in-place ]---- RPM_NO_BIP=1 AC_CHECK_PROG([HAVE_RPM], [rpm], [Yes], [No]) if test $HAVE_RPM = Yes; then AC_CHECK_PROG([HAVE_RPMBUILD], [rpmbuild], [Yes], [No]) RPM_SRC_DIR=`rpm --eval "%{_sourcedir}"` if ! test -d $RPM_SRC_DIR; then HAVE_RPMBUILD=No fi if test $HAVE_RPMBUILD = Yes; then rpmbuild --help | grep -q -- --build-in-place RPM_NO_BIP=$? fi fi AM_CONDITIONAL([RPM], [test $HAVE_RPM = Yes]) AM_CONDITIONAL([RPM_BIP], [test $RPM_NO_BIP -eq 0]) dnl ----[ Determine system init type]---- INIT_TYPE= AS_IF( [test -n "$init_type"], [INIT_TYPE=$init_type], [test -n "$with_systemdsystemunitdir"], [INIT_TYPE=systemd], [ /sbin/init --version 2>/dev/null | grep -q upstart AS_IF( [test $? -eq 0], [INIT_TYPE=upstart], [ init_path=`which systemctl 2>/dev/null` AS_IF([test \( $? -eq 0 -a -x "$init_path" \)], [ systemctl | grep -q -- "-\.mount" AS_IF([test $? -eq 0], [INIT_TYPE=systemd]) ]) AS_IF([test \( -z "$INIT_TYPE" -a -f /etc/init.d/networking \)], [ init_path=`which openrc-run 2>/dev/null` AS_IF([test \( $? -eq 0 -a -x "$init_path" \)], [ head -1 /etc/init.d/networking | grep -q "^#! */.*/openrc-run$" AS_IF([test $? -eq 0], [INIT_TYPE=openrc]) ]) ]) AS_IF([test -z "$INIT_TYPE"], [ for f in /etc/init.d/cron* /etc/init.d/*syslog*; do INIT_TYPE=SYSV break done ]) ]) ]) AS_IF([test .$INIT_TYPE != .], [add_config_opt([INIT=$INIT_TYPE])]) AS_IF([test .$INIT_TYPE = .systemd], [ AS_IF([test -z "$with_systemdsystemunitdir" -o \ .$with_systemdsystemunitdir = .yes -o \ .$with_systemdsystemunitdir = .auto], [ def_systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd` AS_IF([test -z "$def_systemdsystemunitdir"], [ AS_IF([test .$with_systemdsystemunitdir = .yes], [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) with_systemdsystemunitdir=no ], [with_systemdsystemunitdir="$def_systemdsystemunitdir"]) ]) AS_IF([test .$with_systemdsystemunitdir != .no], [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) ]) USE_SYSTEMD_NOTIFY=No SYSTEMD_SERVICE_TYPE=forking SYSTEMD_EXEC_START_OPTIONS= AS_IF([test .$INIT_TYPE = .systemd], [ dnl ----[Check for systemd libraries]---- AS_IF([test .${enable_systemd} != .no], [ $PKG_CONFIG --exists libsystemd AS_IF([test $? -eq 0], [ USE_SYSTEMD_NOTIFY=Yes add_pkg_config([libsystemd]) AC_DEFINE([_USE_SYSTEMD_NOTIFY_], [ 1 ], [Define to 1 if want systemd notify support]) add_config_opt([SYSTEMD_NOTIFY]) SYSTEMD_SERVICE_TYPE="notify" SYSTEMD_EXEC_START_OPTIONS+="--dont-fork" SAV_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include int main(void) { sd_pid_notify(0, 1, ""); } ]])], [AC_DEFINE([HAVE_SD_PID_NOTIFY], [ 1 ], [Define to 1 if you have the `sd_pid_notify` function])]) CFLAGS=$SAV_CFLAGS ], [ AS_IF([test .${enable_systemd} = .yes], [AC_MSG_WARN([libsystemd missing])]) ]) ]) AC_SUBST([SYSTEMD_SERVICE_TYPE]) ]) AM_CONDITIONAL([WITH_SYSTEMD_NOTIFY], [test $USE_SYSTEMD_NOTIFY = Yes]) AC_SUBST([SYSTEMD_EXEC_START_OPTIONS]) dnl ----[Default runtime options (in /etc/sysconfig/keepalived)]---- AC_SUBST([KEEPALIVED_RUNTIME_OPTIONS], ["$default_runtime_options"]) dnl ----[Default keepalived configuration file]---- echo $default_config_file | grep -q '${' AS_IF([test $? -eq 0], [ PARAM=`echo $default_config_file | sed -e 's:.*\(${.*}\).*:\1:'` PARAM_BRACKETS=`echo $PARAM | sed -e "s:{:(:" -e "s:}:):"` AC_MSG_WARN([--with-default-config-file specified with $PARAM - please use $PARAM_BRACKETS]) default_config_file=`echo $default_config_file | sed -e "s:{:(:" -e "s:}:):"` ]) WANT_PREFIX=0 FOUND_PREFIX=0 # $prefix defaults to NONE, which we need to get rid of sysconfdir_real=${sysconfdir} # while [[[ $sysconfdir_real =~ '\${' ]]]; do # eval sysconfdir_real=$sysconfdir_real # done sysconfdir_sav=${sysconfdir_real} eval sysconfdir_real=$sysconfdir_real while [[ $sysconfdir_real != $sysconfdir_sav ]]; do sysconfdir_sav=${sysconfdir_real} eval sysconfdir_real=$sysconfdir_real done AS_IF([test ${sysconfdir_real:0:5} = "NONE/"], [ sysconfdir_real=${sysconfdir_real:4} ]) AS_IF([test -n "$default_config_file"], [ AS_IF([test $default_config_file = yes -o $default_config_file = no], [AC_MSG_ERROR([A filename must be specified for default-config-file])]) FIRST_CHAR=`echo $default_config_file | $SED -e "s/\(.\).*/\1/"` AS_IF([test $FIRST_CHAR = / -o $FIRST_CHAR = '$' ], [ CONFIG_FILE="$default_config_file" WANT_PREFIX=1 ], [ CONFIG_FILE=${sysconfdir_real}/$PACKAGE/$default_config_file ]) add_config_opt([DEFAULT_CONFIG_FILE=$CONFIG_FILE]) default_config_file=$CONFIG_FILE ], [ default_config_file=${sysconfdir_real}/$PACKAGE/$PACKAGE.conf ]) dir=`echo $default_config_file | $SED -e "s|/[[^/]]*$||"` name=`echo $default_config_file | $SED -e "s|.*/||"` AC_SUBST([DEFAULT_CONFIG_FILE], [$default_config_file]) AC_SUBST([DEFAULT_CONFIG_DIR], [${dir}]) AC_SUBST([DEFAULT_CONFIG_FILENAME], [${name}]) # Change any make $(...) variable to the matching shell variable and substitute default_config_file=`echo $default_config_file | $SED -e "s|\\\$(\([[^)]]*\))|\\\${\1}|g"` dcf=$default_config_file dcf_prev=x$dcf # to force at least 1 iteration while [[ $dcf != $dcf_prev ]]; do echo $dcf | grep -q "^\\\${prefix}" AS_IF( [test $? -eq 0], [FOUND_PREFIX=1], [test -n "${prefix}"], [ echo $dcf | grep -q "^${prefix}/" AS_IF([test $? -eq 0], [FOUND_PREFIX=1]) ]) dcf_prev=$dcf dcf=`eval echo $dcf` done AS_IF([test $WANT_PREFIX -eq 1 -a $FOUND_PREFIX -eq 0], [dcf=${prefix}$dcf]) dcf=`echo $dcf | $SED -e "s://*:/:g"` AC_DEFINE_UNQUOTED([DEFAULT_CONFIG_FILE], ["$dcf"], [The default configuration file]) # Remove a leading ${prefix} since that is not part of the old file name eval dcf_old=`echo $default_config_file` dcf_old=`echo $dcf_old | $SED -e "s|^${prefix}||"` AS_IF([test .$dcf != .$dcf_old], [ AC_DEFINE_UNQUOTED([OLD_DEFAULT_CONFIG_FILE], ["$dcf_old"], [The old default configuration file]) AC_SUBST([OLD_DEFAULT_CONFIG_FILE], [$dcf_old]) ]) AS_IF([test .$enable_reproducible_build = .yes], [ AC_DEFINE([_REPRODUCIBLE_BUILD_], [ 1 ], [Make the build reproducible]) KEEPALIVED_CONFIG_OPTIONS="$args" AC_SUBST([KEEPALIVED_CONFIG_OPTIONS]) primary_config_opts=${sysconfdir_real}/$PACKAGE/$PACKAGE.config-opts AC_DEFINE_UNQUOTED([CONFIG_OPTS_FILE_PRIMARY], ["$primary_config_opts"], [Primary file to read build config options from]) ]) AM_CONDITIONAL([REPRODUCIBLE_BUILD], [test .$enable_reproducible_build = .yes]) if test -z "$INIT_TYPE"; then INIT_TYPE=undetected elif test $INIT_TYPE = systemd; then AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir]) fi AM_CONDITIONAL([INIT_UPSTART], [test $INIT_TYPE = upstart]) AM_CONDITIONAL([INIT_SYSTEMD], [test $INIT_TYPE = systemd]) AM_CONDITIONAL([INIT_SYSV], [test $INIT_TYPE = SYSV]) AM_CONDITIONAL([INIT_OPENRC], [test $INIT_TYPE = openrc]) AM_CONDITIONAL([INIT_SUSE], [test $INIT_TYPE = SUSE]) AC_DEFINE_UNQUOTED([CONFIGURATION_OPTIONS], ["$CONFIG_OPTIONS"], [The configuration options from which the package is built]) AC_DEFINE_UNQUOTED([SYSTEM_OPTIONS], ["$SYSTEM_OPTIONS"], [The system options from which the package is built]) if test $NETLINK_VER -eq 0; then NETLINK_VER=None fi dnl ----[ Process output target ]---- echo # Tidy up some strings KA_CPPFLAGS=`echo $KA_CPPFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_CFLAGS=`echo $KA_CFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_LDFLAGS=`echo $KA_LDFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_LIBS=`echo $KA_LIBS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` # Tidy up some strings KA_CPPFLAGS=`echo $KA_CPPFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_CFLAGS=`echo $KA_CFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_LDFLAGS=`echo $KA_LDFLAGS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` KA_LIBS=`echo $KA_LIBS | sed -e "s/ */ /g" -e "s/^ //" -e "s/ $//"` AC_OUTPUT dnl ----[ Display current configuration ]---- cat < export DEBUG_CFLAGS export DEBUG_CPPFLAGS export DEBUG_LDFLAGS edit = echo " EDIT $@"; \ @SED@ -e "/^\[\!\[/d" SUBDIRS = lib keepalived doc SUBDIRS += bin_install EXTRA_DIST = AUTHOR CONTRIBUTORS snap README.md build_setup autogen.sh tools/timed_reload doc_DATA = README MOSTLYCLEANFILES = README MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} README: $(srcdir)/README.md @$(edit) '$(srcdir)/$@.md' >$@ if WITH_DBUS install-data-hook: @if [ @sysconfdir@ != /etc ]; then \ echo; \ echo "NOTE: To enable DBus to work you probably want to add:"; \ echo " @sysconfdir@/dbus1/system.d"; \ echo " to /etc/dbus-1/system-local.conf and restart dbus or reboot."; \ echo; \ echo " It should look something like:"; \ echo; \ echo ""; \ echo ""; \ echo ""; \ echo " @sysconfdir@/dbus-1/system.d"; \ echo ""; \ echo ""; \ echo; \ fi endif distclean-local: @rm -f aclocal.m4 keepalived-$(VERSION).tar.gz config.log config.status @rm -rf autom4te.cache # If we are in a git tree, set the last modified date of each unmodified source file # based on its git commit date. This will help repeatable builds. dist-hook: @if [ -x `type -p git` ]; then \ git rev-parse --is-inside-work-tree >/dev/null 2>&1; \ if [ $$? -eq 0 ]; then \ echo " DATESET git tree"; \ for f in `git ls-tree --full-tree -r --name-only HEAD`; \ do \ if [ -n "`git status --porcelain $$f`" ]; then continue; fi; \ if [ ! -f $(top_distdir)/$$f ]; then continue; fi; \ touch --date=@`git log -n 1 --format=%ct -- $$f` $(top_distdir)/$$f; \ done; \ cd lib; \ make git-commit.h; \ cd ..; \ fi \ fi @rm -f $(distdir)/README .PHONY: docker docker: dist docker build -t keepalived --build-arg GIT_VER=`grep GIT_COMMIT lib/git-commit.h | sed -e 's/.*"v[^-]*\(.*\)"/\1/'` . # clean all files that are generated by automake/autoconf etc autoclean: @$(MAKE) distclean @rm -f configure `find . -name Makefile.in` lib/config.h.in lib/git-commit.h lib/stamp-h[12] m4/pkg.m4 @rm -rf build-aux git-clean: @$(MAKE) autoclean clean-local: clean-local-snap # clean files that are generated by snapcraft .PHONY: clean-local-snap clean-local-snap: -rm -rf parts/ prime/ stage/ keepalived_*.snap # Added targets to maintain compatibility with keepalived releases 1.2.22 and earlier .PHONY: tarball rpm debug profile mrproper tarball: dist @RPM_TRUE@rpm: @RPM_TRUE@ @$(MAKE) dist @RPM_TRUE@ @cp -p keepalived-$(VERSION).tar.gz `rpm --eval "%{_sourcedir}"` @RPM_TRUE@@RPM_BIP_TRUE@ rpmbuild -ba --build-in-place keepalived.spec @RPM_TRUE@@RPM_BIP_FALSE@ rpmbuild -ba keepalived.spec debug: @$(MAKE) DEBUG_LDFLAGS=-ggdb profile: @$(MAKE) DEBUG_CFLAGS=-pg mrproper: @echo Please use `make distclean` AM_DISTCHECK_CONFIGURE_FLAGS = \ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) keepalived-2.3.3/keepalived.spec.in0000664000175000017500000002473214437660462012731 # Ugly, but we need headers from a kernel to rebuild against %define kernel %(rpm -q kernel-devel --qf '%{RPMTAG_VERSION}-%{RPMTAG_RELEASE}\\n' 2>/dev/null | head -1) Summary: HA monitor built upon LVS, VRRP and services poller Name: keepalived Version: @VERSION@ Release: 1%{?dist} License: GPL-2.0-or-later Group: Applications/System URL: http://www.keepalived.org/ Source0: http://www.keepalived.org/software/keepalived-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: openssl-devel # We need both of these for proper LVS support BuildRequires: kernel-headers BuildRequires: autoconf automake pkgconfig BuildRequires: kmod-devel @SNMP_TRUE@BuildRequires: net-snmp-devel @WITH_DBUS_TRUE@BuildRequires: glib2-devel @IPTABLES_TRUE@BuildRequires: iptables-devel @LIBIPSET_TRUE@BuildRequires: ipset-devel @LIBNL1_TRUE@BuildRequires: libnl-devel @LIBNL3_TRUE@BuildRequires: libnl3-devel @NFTABLES_TRUE@BuildRequires: libnftnl-devel libmnl-devel @INIT_SYSV_TRUE@Requires(post): /sbin/chkconfig @INIT_SYSV_TRUE@Requires(preun): /sbin/service, /sbin/chkconfig @INIT_SYSV_TRUE@Requires(postun): /sbin/service @INIT_SYSTEMD_TRUE@BuildRequires: systemd-units @INIT_SYSTEMD_TRUE@BuildRequires: systemd-devel @WITH_REGEX_TRUE@BuildRequires: pcre2-devel @MAGIC_TRUE@BuildRequires: file-devel Requires: openssl Requires: kmod-libs @SNMP_TRUE@Requires: net-snmp @WITH_DBUS_TRUE@Requires: glib2 @IPTABLES_TRUE@%if 0%{?rhel} <= 7 @IPTABLES_TRUE@Requires: iptables @IPTABLES_TRUE@%else @IPTABLES_TRUE@Requires: iptables-libs @IPTABLES_TRUE@%endif @LIBIPSET_TRUE@Requires: ipset-libs @LIBNL1_TRUE@Requires: libnl @LIBNL3_TRUE@Requires: libnl3 @NFTABLES_TRUE@Requires: libnftnl libmnl @WITH_REGEX_TRUE@Requires: pcre2 %description The main goal of the keepalived project is to add a strong & robust keepalive facility to the Linux Virtual Server project. This project is written in C with multilayer TCP/IP stack checks. Keepalived implements a framework based on three family checks : Layer3, Layer4 & Layer5/7. This framework gives the daemon the ability to check the state of an LVS server pool. When one of the servers of the LVS server pool is down, keepalived informs the linux kernel via a setsockopt call to remove this server entry from the LVS topology. In addition keepalived implements an independent VRRPv2 stack to handle director failover. So in short keepalived is a userspace daemon for LVS cluster nodes healthchecks and LVS directors failover. %prep %setup %build CONFIG_OPTS= @DEBUG_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-debug" @PROFILE_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-profile" @WITH_VRRP_FALSE@CONFIG_OPTS="$CONFIG_OPTS --disable-vrrp" @WITH_IPVS_FALSE@CONFIG_OPTS="$CONFIG_OPTS --disable-lvs" @NFTABLES_FALSE@CONFIG_OPTS="$CONFIG_OPTS --disable-nftables" @IPTABLES_FALSE@CONFIG_OPTS="$CONFIG_OPTS --disable-iptables" @IPTABLES_TRUE@@LIBIPSET_FALSE@CONFIG_OPTS="$CONFIG_OPTS --disable-libipset" @LIBIPTC_DYNAMIC_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-libiptc-dynamic" @LIBIPSET_TRUE@@LIBIPSET_DYNAMIC_FALSE@CONFIG_OPTS="$CONFIG_OPTS --disable-libipset-dynamic" @LIBNL_DYNAMIC_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-libnl-dynamic" @SNMP_KEEPALIVED_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-snmp" @SNMP_KEEPALIVED_FALSE@@SNMP_VRRP_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-snmp-vrrp" @SNMP_KEEPALIVED_FALSE@@SNMP_CHECKER_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-snmp-checker" @SNMP_RFC_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-snmp-rfc" @SNMP_RFC_FALSE@@SNMP_RFCV2_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-snmp-rfcv2" @SNMP_RFC_FALSE@@SNMP_RFCV3_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-snmp-rfcv3" @SNMP_RFC_TRUE@@SNMP_REPLY_V3_FOR_V2_FALSE@CONFIG_OPTS="$CONFIG_OPTS --disable-snmp-reply-v3-for-v2" @WITH_DBUS_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-dbus" @WITH_JSON_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-json" @INIT_UPSTART_TRUE@CONFIG_OPTS="$CONFIG_OPTS --with-init=upstart" @INIT_SYSTEMD_TRUE@CONFIG_OPTS="$CONFIG_OPTS --with-init=systemd" @INIT_SYSV_TRUE@CONFIG_OPTS="$CONFIG_OPTS --with-init=SYSV" @INIT_SUSE_TRUE@CONFIG_OPTS="$CONFIG_OPTS --with-init=SUSE" @INIT_OPENRC_TRUE@CONFIG_OPTS="$CONFIG_OPTS --with-init=openrc" @WITH_REGEX_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-regex" @WITH_BFD_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-bfd" @WITH_STRICT_CONFIG_CHECKS_TRUE@CONFIG_OPTS="$CONFIG_OPTS --enable-strict-config-checks" autoreconf -f -i %configure $CONFIG_OPTS %{__make} %{?_smp_mflags} STRIP=/bin/true %install %{__rm} -rf %{buildroot} %{__make} install DESTDIR=%{buildroot} # Remove "samples", as we include them in %%doc %{__rm} -rf %{buildroot}%{_sysconfdir}/keepalived/samples/ # Likewise remove README %{__rm} -f %{buildroot}%{_docdir}/%{name}/README # Copy the doc files we want, removing the .pem files %{__mkdir} -p doc_install/samples %{__cp} doc/keepalived.conf.SYNOPSIS doc_install %{__cp} doc/samples/* doc_install/samples %{__rm} -f doc_install/samples/*.pem # Rename the SYSV init file if necessary @INIT_SUSE_TRUE@%{__mv} %{buildroot}%{_initrddir}/keepalived.suse.init %{buildroot}%{_initrddir}/keepalived @INIT_UPSTART_FALSE@%{__rm} -f %{buildroot}%{_sysconfdir}/init/keepalived.conf %check # A build could silently have LVS support disabled if the kernel includes can't # be properly found, we need to avoid that. if ! grep -q "#define _WITH_LVS_ *1" lib/config.h; then %{__echo} "ERROR: We do not want keepalived lacking LVS support." exit 1 fi %clean %{__rm} -rf %{buildroot} %{__rm} -rf doc_install %post if [ $1 -eq 1 ]; then # Enable (but don't start) the units by default @INIT_SYSV_TRUE@ /sbin/chkconfig --add keepalived @INIT_SYSTEMD_TRUE@ /bin/systemctl enable keepalived.service >/dev/null 2>&1 || : : fi %preun if [ $1 -eq 0 ]; then # Disable and stop the units @INIT_SYSV_TRUE@ /sbin/service keepalived stop &>/dev/null || : @INIT_SYSV_TRUE@ /sbin/chkconfig --del keepalived @INIT_SYSTEMD_TRUE@ /bin/systemctl disable keepalived.service >/dev/null 2>&1 || : @INIT_SYSTEMD_TRUE@ /bin/systemctl stop keepalived.service >/dev/null 2>&1 || : @INIT_UPSTART_TRUE@ /sbin/stop keepalived >/dev/null 2>&1 || : : fi %postun if [ $1 -ge 1 ]; then @INIT_SYSV_TRUE@ /sbin/service keepalived condrestart &>/dev/null || : # On upgrade, reload init system configuration if we changed unit files # and restart the daemon @INIT_SYSTEMD_TRUE@ /bin/systemctl daemon-reload >/dev/null 2>&1 || : @INIT_SYSTEMD_TRUE@ /bin/systemctl try-restart keepalived.service >/dev/null 2>&1 || : : fi %files %defattr(-, root, root, -) %doc AUTHOR ChangeLog CONTRIBUTORS COPYING README TODO %doc doc_install/* %dir %{_sysconfdir}/keepalived/ %attr(0600,root,root) %{_sysconfdir}/keepalived/keepalived.conf.sample %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/sysconfig/keepalived @INIT_SYSTEMD_TRUE@%{_unitdir}/keepalived.service @INIT_SYSV_TRUE@%{_initrddir}/keepalived @INIT_UPSTART_TRUE@%{_sysconfdir}/init/keepalived.conf @SNMP_TRUE@%{_datadir}/snmp/mibs/KEEPALIVED-MIB.txt @SNMP_RFCV2_TRUE@%{_datadir}/snmp/mibs/VRRP-MIB.txt @SNMP_RFCV3_TRUE@%{_datadir}/snmp/mibs/VRRPv3-MIB.txt @WITH_IPVS_TRUE@%{_bindir}/genhash %attr(0755,root,root) %{_sbindir}/keepalived @WITH_IPVS_TRUE@%{_mandir}/man1/genhash.1* %{_mandir}/man5/keepalived.conf.5* %{_mandir}/man8/keepalived.8* @WITH_DBUS_TRUE@%attr(0644,root,root) %{_sysconfdir}/dbus-1/system.d/org.keepalived.Vrrp1.conf @WITH_DBUS_TRUE@%attr(0644,root,root) %{_datarootdir}/dbus-1/interfaces/org.keepalived.Vrrp1.Instance.xml @WITH_DBUS_TRUE@%attr(0644,root,root) %{_datarootdir}/dbus-1/interfaces/org.keepalived.Vrrp1.Vrrp.xml %changelog * Mon Jan 24 2022 Quentin Armitage 2.2.7-2 - RedHat has kmod-lib and kmod-devel rather than libkmod and libkmod-devel * Mon Jan 24 2022 Quentin Armitage 2.2.7-1 - Change to install /etc/keepalived/keepalived.conf.sample instead of keepalived.conf * Wed Nov 1 2017 Quentin Armitage 1.3.9-1 - Fix installation of keepalived.service * Tue Oct 17 2017 Quentin Armitage 1.3.8-1 - Handle Fedora and CentOS differences for %{_docdir_fmt} * Fri Sep 16 2016 Quentin Armitage 1.2.24-2 - Fixes to allow building on a systemd based system * Wed Sep 14 2016 Quentin Armitage 1.2.24-1 - Add more BuildRequires * Tue Sep 13 2016 Quentin Armitage 1.2.24 - Update for changed format due of config.log due to using automake - Add support for systemd and upstart based systems * Thu Sep 13 2007 Alexandre Cassen 1.1.14 - Merge work done by freshrpms.net... Thanks guys !!! ;) * Wed Feb 14 2007 Matthias Saou 1.1.13-5 - Add missing scriplet requirements. * Tue Feb 13 2007 Matthias Saou 1.1.13-4 - Add missing \n to the kernel define, for when multiple kernels are installed. - Pass STRIP=/bin/true to "make" in order to get a useful debuginfo package. * Tue Feb 13 2007 Matthias Saou 1.1.13-3 - Add %%check section to make sure any build without LVS support will fail. * Mon Feb 5 2007 Matthias Saou 1.1.13-2 - Use our own init script, include a sysconfig entry used by it for options. * Thu Jan 25 2007 Matthias Saou 1.1.13-1 - Update to 1.1.13. - Change mode of configuration file to 0600. - Don't include all of "doc" since it meant re-including all man pages. - Don't include samples in the main configuration path, they're in %%doc. - Include patch to add an optional label to interfaces. * Sat Apr 08 2006 Dries Verachtert - 1.1.12-1.2 - Rebuild for Fedora Core 5. * Sun Mar 12 2006 Dag Wieers - 1.1.12-1 - Updated to release 1.1.12. * Fri Mar 04 2005 Dag Wieers - 1.1.11-1 - Updated to release 1.1.11. * Wed Feb 23 2005 Dag Wieers - 1.1.10-2 - Fixed IPVS/LVS support. (Joe Sauer) * Tue Feb 15 2005 Dag Wieers - 1.1.10-1 - Updated to release 1.1.10. * Mon Feb 07 2005 Dag Wieers - 1.1.9-1 - Updated to release 1.1.9. * Sun Oct 17 2004 Dag Wieers - 1.1.7-2 - Fixes to build with kernel IPVS support. (Tim Verhoeven) * Fri Sep 24 2004 Dag Wieers - 1.1.7-1 - Updated to release 1.1.7. (Mathieu Lubrano) * Mon Feb 23 2004 Dag Wieers - 1.1.6-0 - Updated to release 1.1.6. * Mon Jan 26 2004 Dag Wieers - 1.1.5-0 - Updated to release 1.1.5. * Mon Dec 29 2003 Dag Wieers - 1.1.4-0 - Updated to release 1.1.4. * Fri Jun 06 2003 Dag Wieers - 1.0.3-0 - Initial package. (using DAR) keepalived-2.3.3/aclocal.m40000664000175000017500000012651114772274254011177 # generated automatically by aclocal 1.16.5 -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.71],, [m4_warning([this file was generated for autoconf 2.71. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # Copyright (C) 2002-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.5], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.5])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # Copyright (C) 2011-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_AR([ACT-IF-FAIL]) # ------------------------- # Try to determine the archiver interface, and trigger the ar-lib wrapper # if it is needed. If the detection of archiver interface fails, run # ACT-IF-FAIL (default is to abort configure with a proper error message). AC_DEFUN([AM_PROG_AR], [AC_BEFORE([$0], [LT_INIT])dnl AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([ar-lib])dnl AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false]) : ${AR=ar} AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface], [AC_LANG_PUSH([C]) am_cv_ar_interface=ar AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])], [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([am_ar_try]) if test "$ac_status" -eq 0; then am_cv_ar_interface=ar else am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([am_ar_try]) if test "$ac_status" -eq 0; then am_cv_ar_interface=lib else am_cv_ar_interface=unknown fi fi rm -f conftest.lib libconftest.a ]) AC_LANG_POP([C])]) case $am_cv_ar_interface in ar) ;; lib) # Microsoft lib, so override with the ar-lib wrapper script. # FIXME: It is wrong to rewrite AR. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__AR in this case, # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something # similar. AR="$am_aux_dir/ar-lib $AR" ;; unknown) m4_default([$1], [AC_MSG_ERROR([could not determine $AR interface])]) ;; esac AC_SUBST([AR])dnl ]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE="gmake" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl m4_ifdef([_$0_ALREADY_INIT], [m4_fatal([$0 expanded multiple times ]m4_defn([_$0_ALREADY_INIT]))], [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi AC_SUBST([CTAGS]) if test -z "$ETAGS"; then ETAGS=etags fi AC_SUBST([ETAGS]) if test -z "$CSCOPE"; then CSCOPE=cscope fi AC_SUBST([CSCOPE]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR m4_include([m4/as-ac-expand.m4]) m4_include([m4/pkg.m4]) keepalived-2.3.3/ChangeLog0000664000175000017500000173634614004226302011102 2020-06-13 Alexandre Cassen This file is now deprecated and wil no longer get updated. To have detailed informations about new Keepalived release please refers to : https://www.keepalived.org/release-notes 2020-01-22 Alexandre Cassen * keepalived-2.0.20 * Add DBus interface and config files to snap * Install DBus files from snap onto host system. * Fix snap install hook script. * Set instance_name in child processes after reload Although the parent process was reinstating the instance_name after a reload, the child processes were not. * Remove libxtables-dynamic from keepalived.spec.in Commit 72c4e54 - "Add support for using iptables to block VMAC IGMP/MLD messages" removed libxtables-dynamic option, since it was only used to load the ip_tables/ip6_tables kernel modules, and there was already other code to load modules. The original commit, however, omitted to update keepalived.spec.in. * Include firewalld documentation. * Improving logging of error if kernel doesn't support PROC_EVENTS OpenWRT has kernels build with PROC_EEVNS enabled, and it was outputing an unhelpful error message. This commit will not log the PROC_EVENTS is not built into the kernel unless the log detail (-D) option is specified, or the configuration uses track_process. * Resolve file descriptor errors on reload If a read or write thread was on the thread reaady queue when a reload was processed, the file descriptor was not removed from the epoll instance. This commit ensures that file descriptors relating to threads on the thread ready queue are removed from the epoll instance during a reload. * Log correct address family when interface doesn't have required address. * Stop track_process.h including itself. * Update package requirements for Debian. * Add more detailed track-process debugging. * Fix track_process with PIDs > 32767 * Add more track_process debugging. * Correct detection of success loading module xt_set. The detection of success loading the module was reversed, so that if it failed it thought it was successful, and vice versa. This meant that if it was not successful loading the module, it would attempt to use ipsets, and if it was successful loading the module it would not. If the module was already loaded, then there was no problem. * Update info on using nft --debug. * Add README icon for snaps. * Fix intermittent "child lost" messages. Issue #1364 identified that occassionaly a "child lost" message could be logged. Although keepalived continued working as expected, the "child lost" message indicated that something wasn't working properly. If a vrrp track script had a timeout in the script that was the same as the script timeout configured in keepalived, when the system was heavily loaded it was possible for the timeout to occur, followed by the termination before the timeout thread was run, in which case the termination would be lost because the child thread was no longer on the child_pid queue, but on the ready queue. This commit leaves threads on the child_pid queue after a timeout, and only removes it when the timeout thread is run. That means that if the termination is received before the timeout thread is run, the thread (now on the ready queue) can be updated to be a termination rather than a timeout. * Add configuration/state dumping for BFD with SIGUSR1. The VRRP and checker processes dump their configuration/state, and this commits and the same for the BFD process. * Stop always going to fault state on reload if have bfd tracker. On a reload, the state of a new bfd tracker should be set from the state of the old matching tracker, rather than the other way around. * Resolve some issues identified by LGTM. * Clarify in man page when ip_family is required for virtual servers. * Don't check _WITH_LVS_ defined in chack_data.h If check_data.h is being included anywhere, then _WITH_LVS_ must be defined. * Stop checking that persistence_granularity is a solid netmask for IPv4 ipvsadm and the kernel do not require a solid netmask, so we shouldn't either. * Allow persistence_granularity to be 0.0.0.0. ipvsadm allows the netmask to be 0.0.0.0 and the kernel supports it, so we should allow it. It is inconsistent with IPv6 which doesn't allow a mask length of 0. * Fix ipaddresstos when it is passed a buffer to write string to Although ipaddresstos() was not correct, since it was never passed a buffer, the problem never manifested itself. * Stop segfault at reload when removing a ip rule without from address Issue #1436 identified that removing an ip rule was causing a segfault when keepalived reloaded. This is caused by always logging the from address even if there wasn't one. * Make bfd0 initialisation structure static. * Ensure bfd thread_time_to_wakeup() doesn't return "negative" value. If thread_time_to_wakeup() was called with thread->sands in the past compared to time_now, the function overflowed and returned an extremely high value, resulting in the thread not being scheduled for another 585,000 years or so (on 64 bit systems). This commit makes thread_time_to_wakeup() return 1 (microsecond) if thread->sands is earlier than time_now. * Make bfd's sand_out, sands_exp and sands_rst unsigned. * Make timer_add_long return TIMER_DISABLED if called with TIMER_NEVER. * Fix generation of keepalived.spec when libipset disabled. * Fix building with --enable-debug option, but add deliberate error Issue #1444 identified that keepalived would not build with the --enable-debug option. This commits resolves the compilation errors but also adds a #error statement if --enable-debug is selected, so that the source code has to be editted to be able to build with the option. Since --enable-debug is not expected to work properly and is only for debugging purposes, the #error statement will stop it being used accidentally or being enabled in a distro's build of keepalived, for example as Gentoo had done. Only developers/maintainers who are prepared to edit the source code will be able to use --enable-debug. * Make json_writer.c include "assert_debug.h" rather than This means that the assert() code will only be enabled if the --enable-asserts configure option is specified. * Correct description of notify_master_rx_lower_pri in keepalived.conf(5). * Reorganise debug configuration --enable-debug enabled several different sets of debugging functionality. That is now split out into: --enable-genhash-debug --enable-checker-debug --enable-smtp-connect-debug --enable-mem-err-debug --enable-one-process-debug All options except --enable-one-process-debug also require enabling at runtime. This means that a debug version of keepalived cam be build with almost all debugging built in, but different debugging can be selectively enabled at run time, to avoid being overwhelmed with unwanted debug output. The --enable-debug option now enables all debugging options except --enable-genhash-debug --enable-one-process-debug This is essentially the debug options that can be controlled at run time. It also sets --enable-log-file so that logging output can be written to log files rather than syslog. keepalived -h shows all the built-in debugging options and how to enable them. * Turn of checksum debugging unless enabled from command line. * Clear previous parser errors at startup or when reloading When keepalived starts, the parent process reads the config. If there was an error such as a missing } or extra { the skip_block flags was left set, so when the child processes read the configuration, they just skipped everything. This also meant that the configuration was not correctly parser when reloading. All parser state variables are now reinitialised before the configuration is read. * Fix a checker process segfault when reloading with ha_suspend If ha_suspend was enabled, when the checker process reloaded, it left an old pointer to the read thread for the netlink monitoring fd. When the process subsequently terminated, it tried cancelling the thread using the old pointer, and would often segfault. This also meant that after a reload, the checker process would no longer be monitoring address changes, in other words ha_suspend would not work. Before a reload, both for the vrrp and the checker process, the thread is cancelled and the pointer to the thread cleared. After a reload the checker process now adds a new read thread, so ha_suspend will continue working after the reload. * Add signal to trigger thread dump when built with --enable-thread-dump. * Update snapcraft.yaml to remove Ubuntu 14.04 and add 19.10 kernel. * Fix building snaps for 5.3 kernel. * snap: Fix checking for s390x. * snap: Improving logging of kernel header version in snap build logs. * snap: Add missing in snap logs. * Update nopreempt documentation in keepalived.conf(5) man page Issue #1457 suggested that the documentation for nopreempt was not clear, so this update attempts to clarify the situation. * Fix building with network-timestamp without checksum-debug Commit 844e197 - "Reorganise debug configuration" had an incorrect conditional compilation check, which this commit resolves. * Fix VRRP priority after weighted track files leaving fault state Issue #1460 identified that the VRRP priority was incorrect after a weighted track file exited fault state. This commit resolves the issue. * Correct typo in keepalived.conf(5). * Fix DNS_CHECK when name ends with a '.'. Issue #1462 identified that DNS_CHECK was not working with name example.jp. It transpired that if the name ended with a '.', the terminating zero length was not being written to the query packet. * Fix generation of keepalived.spec An AM_CONDITIONAL was missing for DEBUG * Fix vrrp dont_track_primary. VIPs on the primary interface were causing the interface to be tracked. keepalived now checks when adding an interface for tracking VIPs or eVIPs that it is not the primary interface of the vrrp instance if dont_track_primary is set. * fix: unknown keyword 'mh-fallback' and 'mh-port'. * Fix detecting of kill failure for MISC_CHECK scripts github @lankstra pointed out that the check for kill failing for misc check scripts was inverted, so this commit corrects that, and the code now matches the similar code in vrrp_script_child_thread(). * Update Dockerfile 1. Update to use alpine:3.10 as the base container 2. Include libnftnl-dev in the build to support nftables 3. Install automake package for building keepalived 4. Run build_setup The reason for installing automake and running build_setup is that if the version of automake is different from what is installed in the container, then the links set up by automake --add-missing on the host will not work in the container used for the build. * Actually update docker to use alpine:3.10. * Resolve some warnings when building with ulibc. * Handle a newer libnftnl with an older kernel To use NFT_LOOKUP_F_INV we need to check that both it and NFTNL_EXPR_LOOKUP_FLAGS are defined. Previously we only checked the latter. * Change order of IPVS commands on quorum change to allow a sorry server to be the same as a real server. * Further code to hanve sorry server being same as a real server Commit 6f03bb2 - "Change order of IPVS commands on quorum change" allowed a sorry server to be the same as a real server. This commit now adds some additional code to ensure that it operates without errors being reported. * Check, report and handle a duplicate real server on a virtual server. * Change some log_message()s to report_config_error()s in IPVS code. * Cosmetic alignment corrections in check_data.h. * Tidy up a Linux version comment in configure.ac. * Remove configure check for SCHED_RR. It has existed since at least Linux 2.4.32. * Add README.kernel_versions to document kernel dependant features. * Add tools/timed_reload This script makes keepalived reload its configuration at a specified time. If there are several keepaliveds running on different systems, then can be scheduled to all reload at the same time, by running the script on each system. This is useful if the old and new configurations are not compatible with each other. * Add option to set preferred lifetime for static and virtual IPv6 addresses. Generally virtual ip addresses will want to be deprecated, but static IPv6 addresses may or may not want to be deprecated. Previously the code deprecated IPv6 addresses only if that had a /128 mask. This commit retains the old settings as default, but now allows the preferred_lft to be specified for each address. 2019-10-19 Alexandre Cassen * keepalived-2.0.19 * Allow persistence, scheduler and flags of VS to be changed on reload A virtual server is identified by its IP address, protocol and port, or the firewall mark and address family, and not by the persistence settings or scheduler and scheduler flags. When comparing virtual servers on a reload, don't check persistence and scheduler settings match, but update them if necessary. * Ignore default RS settings when comparing VSs after reload Various default settings for real servers belonging to a virtual server can be configured against a virtual server. These settings should be ignored when comparing virtual servers following a reload. Any differences in real server settings will be handled separately. * Clarify what IPVS persistence engines are supported in man page * Allow RS forwarding method to be changed on reload A real server is identified by its IP address and port, and not by the forwarding method. When comparing real servers on a reload, don't check the forwarding method, but update it if necessary. * Check address family when comparing fwmark VSs on reload * Update test tcp server * Allow more than one BFD instance with a neighbour This commit now checks both the neighbour address and the source/local address when finding a BFD instance. This means that more than one BFD instance can be set up with the same neighbour, so long as a different local address is used. * Make PID files group and world readable Issue #1378 identified that PID files were created without group and work read in the file permission bits being set, due to the umask. This was causing a problem, since a non-root user was needing to read the PID file. This commit now forces the file permissions of PID files to be: owner=rw, group=r, other=r. * Fix erroneous error message when creating IPv4 ipvlan interfaces The code was checking for a return value != -1 to identify an error, whereas it should have been checking for return value != 1. * Ignore reloading signals till signal_init call * fix bug in vrrp_json_track_ifp_dump() * Fix handling unknown user in MISC_CHECK If the user was unknown, it wasn't dequeuing the new checker, resulting in a subsequent coredump. * Fix ~SEQ handling Commit 47b2207 - "Add const attribute where appropriate in config parser code" broke handling of ~SEQ, due to including one extra character at the end of the sequence variable name. This commit corrects the length of the variable set as part of the ~SEQ processing. * Revert "Fix route add/delete on reload if only change via address" This reverts commit f54c2e8294c6f2080c3ae951ba25bf40d5b8d211. The commit did the wrong thing: the via address is not part of the key of the route. The problem was that if it detected it already had a route with the same key, it replaced the route, but with the old route and not the new one. The next commit will rectify this. * Correct handling of replacing routes on reload Issue #1390 identified that a route with a changed source address wasn't being changed on reload, and identified that the problem was related to issue #1220. It turns out that commit f54c2e8 which resolved issue #1220 was not the correct fix. The problem was that when reloading, if a new route matched an old route, the old route was replaced with the OLD route (i.e. it did nothing), rather than replacing it with the NEW route. This commit now replaces the old route with the new route. * Add addresses/routes/rules in that order and delete in reverse order Routes can requires addresses to exist in order to be able to add them, and rules determine which routes are used. * Fix not logging error on deletion of expired route * Make netlink_route() return bool, and add some LIST_FOREACH * Handle changing virtual route to use new VIP on reload During a reload, routes replacing existing routes are replaced, as opposed to deleted and added; this avoids the route disappearing for a short while. However, if a new route uses a VIP that didn't exist in the old configuration then the route replacement will fail. The code is now changed so that it attempts to replace the route, but if that fails, it deletes the old route and later adds the new route after the new VIPS have been added. * Don't log EPOLLERR or EPOLLHUP - they can occur with TCP_CHECK * Fix tcp_server getopt() handling * Properly handle MISC_CHECK script returning exit status 0 If a MISC_CHECK script returned an exit status >= 2, and then the script returns 0, the weight of the real server was not updated following the script returning 0, and the quorum also wasn't updated. This commit makes keepalived update the weight of a real server and the quorum following a MISC_CHECK returning a 0 exit status. This is a change of behaviour for MISC_CHECKs whose scripts return an exit status >= 2 subsequently followed by an exit status of 0. However, the new behaviour is consistent with the documentation and is also the behaviour that would be expected. Some users may need to modify their MISC_CHECK scripts if the scripts return an exit code >= 2 and subsequently return an exit code of 0 expecting that to mean that the status hasn't changed. * Correct documentation re range of values for Virtual Router ID The valid range of values for the VRID is 1 to 255, and does not include 0. * Handle script timeouts when child process has terminated Issue #1364 identified that if a track script timed out and the kill of the process failed to to its exit status already having been reaped, keepalived would never run the track scripts again. It transpires that the same problem existed with MISC_CHECK scripts. The commit now ensures that after the timeout the script will be set to idle state, so that it can be run again. * Clear thread_master current_event when cleanup thread_master The current_event was left pointing to an event that had been deleted, with the consequence that if the next epoll event matched the outdated current_event, that outdated event could be used. This commit clears the current_event when the thread_master is cleaned up. * Add errno numbers to some bfd error messages, rather than just text Although the strerror() text is helpful, it is often useful just to know the error number. * Enable FIFOs to receive initial fault notifications at startup Issue #1399 identified that if a track_file caused a VRRP instance to start in fault state, there was no notification of the fault sent to the notify FIFOs. This commit implements the suggestion of chenwng in issue #1399 and moves the opening of the notify FIFOs to earlier in the init process. * Resolve incorectly identified Coverity error * Define VRRP and BFD initialisms * Add support for nftables blocking IGMP/MLD packets on VMAC interfaces Issue #882 identified that VRRP instances using VMACs sent IGMP/MLD packets using the MAC address of the VMAC interface even when the vrrp instance was in backup state. This meant that switches would update what interface the VRRP MAC address was on to the backup instance, thereby meaning that until the master instance sent another advert, packets destined to be forwarded by the master instance would be delivered to the backup instance and lost. This commit adds support to use nftables to stop the packets being send from the VMAC interface (and thereby using the 00:00:5e:00:0x:xx MAC address), and instead the packets are sent on the parent interface. How this is acheived depends on whether the kernel supports the dup statement; if it does the IGMP messages are simply moved from the VMAC interface to the parent interface otherwise the join groups are done on both the VMAC interface and the parent interface, and nftables drops the join messages on the VMAC interface. This functionality might be better implemented using eBPF, but older systems don't support that, and I still need to work out how to use eBPF. * Add support for using iptables to block VMAC IGMP/MLD messages Commit b10bbfc2a added support for using nftables to block IGMP/MLD messages being sent VMAC interfaces. This commit adds the equivalent functionality using iptables. * Improve checking of incompatible configure options * Fix Travis-CI build errors * Don't attempt to remove IGMP blocking iptables rules for IPVLANs We don't block IGMP/MLD for IPVLANs so there are no rules to remove. * Turn off nftables mnl debug logging * Don't log an error when deleting nonexistant nftable at startup Since there is no native flush command to the kernel to delete all the rules, sets, meters etc in a table, we just delete the table, and if the table doesn't exist an error is returned. This commits stops the error being logged, since it isn't an error of interest. * Minor tidying up of setting up nftables * Fix error when setting up nftables with eVIPs from other family The sequence number of nftables netlink messages was getting out of order when a VRRP instance had both IPv4 and IPv6 entries. This is now resolved by checking for the existance of residual tables of both families when keepalived first checks for residual tables. * Remove some duplicate nftables code * Remove inhibit_on_failure from keepalived.conf(5) man page The inhibit_on_failure keyword does not apply to checkers, and is not implemented for them. * Remove vi swap file erroneously included in previous commit * Add additional logging of reasons for vrrp instances going to fault Pull request #1353 suggested adding additional logging for reasons for vrrp instances going to fault state at startup, and for track file status changes. This commit merges those changes, fixes one error, and only logs track file status changes if the -D option (log detail) is set. * Make skip_check_adv_addr work properly Keepalived was checking the received advert packet length against the expected length based on the number of VIPs configured on the vrrp instance. This commit changes the check so that the received packet length is checked against the number of VIPs in the VRRP packet header, thereby ensuring that the advert packet is self consistent. The check for the number of addresses is now only done if skip_check_adr_addr is not set (which matches what the RFC says). Note: skip_check_adv_addr only skips the check of VIPs in a received packet if the advert is received from the same master as the previous advert. With this commit, if skip_check_adv_addr is set, it is possible to reload a master instance with a different number of VIPs, and then subsequently reload the backup instance, without the backup instance becoming master due to the mismatch of the number of VIPs. 2019-07-26 Alexandre Cassen * keepalived-2.0.18 * Set NA_ROUTER flag in gratuitous NA messages appropriately. Previously keepalived checked the IPv6 forwarding state of the interface/ parent interface of a VRRP instance, and used that for all GNA messages. However, if addresses are configured on different interfaces, it should be the setting for the address's interface that is used. * Fix memory leak with dbus_instance_name. * Make set_value() add entry for memcheck identifying where called. * Add configure option --enable-checksum-debug. Issue #1175 identified that intermittently they were getting VRRPv3 checksum errors. The maintainers of keepalived were unable to reproduce the problem despite extensive testing, and so a special patch was produced to check and log any checksum changes from previous adverts sent or received. Almost two months later there has been no feedback. The patch has now been forward ported from v2.0.12 to v2.0.17 and is included here, enabled by --enable-checksum-debug option, so that if there are ever any checksum problems in the future this code can be used to ascertain what is happening. * Fix configuring LVS sync daemon in backup state. Commit eb929f8 - "Stop LVS sync daemon on shutdown" moved shutting down the LVS sync daemon to the wrong place, so that it was called whenever a VRRP instance transitioned out of master state. This commit moves the shutting down of the sync daemon to shutdown phase 1, and it is shutdown before the VRRP instances are shut down. * Increase open file limit for checker process if no of checkers need it. TCP, HTTP/SSL, DNS and SMTP checkers all use a socket. If there is a sufficiently large number of checkers, the default open file limit may be exceeded. This commits counts the number of such checkers, and also thr number of smtp_alerts, and if necessary increases the open file limit to allow them all to run at once. * Ensure MISC_CHECK processes don't get increase open file limit. * When checking number of open files for vrrp process, allow for smtp alerts. * Combine checker set_max_file_limit() and set_vrrp_max_fds() common code. * DNS_CHECK: correct error info in dns_type_handler func. Sometimes, users set two type values by mistake in keepalived.conf, and the first is right and the second one is not in DNS_TYPE[]. Then the dns_check->type is set successfully when parsing first type value , which may be different from the default SOA. As for the second one, the dns_type_handler func will print error info "Defaulting to SOA", actually, currently the dns_check->type may be not equal to SOA. Here, we will print the dns_type_name(dns_check->type) instead of "SOA". * Simplify restoring RLIMIT_NOFILE for child processes. * Simplify handling incorrect dns_check type. * Add missing track_process documentation to keepalived.conf(5) man page. * Add weight "reverse" feature to track_bfd. The reverse feature allows reducing the priority when the tracker is up and reducing the priority when the tracker is down. * Add weight "reverse" feature to track_interface. The reverse feature allows reducing the priority when the tracker is up and reducing the priority when the tracker is down. * Add weight "reverse" feature to track_script. The reverse feature allows reducing the priority when the tracker is up and reducing the priority when the tracker is down. * Update alloc_track_file() and alloc_group_track_file() to be consistent. * Allow reverse tracking with weight 0. This allows a vrrp instance to go to fault state if an interfaces is UP, or a track script or bfd instance is up, or a track process has achieved quorum, and down otherwise. * Fix reverse on track_script when configured on sync group and instance If a track script was configured on both a vrrp instance and the sync group that the instance was configured in, then the reverse setting wasn't being properly carried forward. * Add weight "reverse" feature to track_file. The reverse feature allows reducing the priority when the tracker is up and increasing the priority when the tracker is down. * Make track_bfd reverse handling consistent with other trackers. * Add track weight reverse to SNMP output. * Add vrrp track_bfd details to SNMP output. * Add vrrp track_process details to SNMP output. * Disallow --enable-track-process-debug with --disable-track-process. * Add conditional compilation around track_bfd/process SNMP code. * Remove duplicate code for parsing vrrp and sync group trackers. The code for parsing trackers for vrrp instances and sync groups was to all intents and purposes identical, so this commit now uses common code for both of them. * sll_protocol should be set to 0x806. Some times , send the gratuitous ARP message should set sll_protocol, let some drivers can evaluate which protocol we use. * Neighbor discovery set sll_protocol. * Fix SNMP VRRPv3 IP address OIDs returned. The OIDs returned for SNMPv3 addresses were incorrectly formatted, including one extra subid that was the length of the IP address. * Don't use numeric values of address lengths for VRRP SNMP v3. * Stop returning not-accessible fields for v2 SNMP. * Stop return not-accessible fields for v3 SNMP. * Use common code for VRRP tracker SNMP output. Many functions were using the same, fairly large, code block to do the same thing. These are now standardised to use the new function snmp_find_element(). * make some vrrp snmp function parameters const. * Make virtual_server_t vsgname const. * Fix SNMP reporting of virtual server group fwmark and address ranges. * More SNMP fixes for virtual server group fwmark and address ranges. * If virtual server is fwmark and rs's tunnelled, default to IPv4. If a virtual server uses a fwmark, and all the real servers are tunnelled, the address family could be IPv4 or IPv6. If the family is not specified, default to IPv4 (to match behavious of ipvsadm). * Make LIST_SIZE safe to use if list is not assigned. * Optimisations to snmp_header_list_table(). * Optimisations to snmp_find_element(). * Further optimisation to snmp_find_element(). * Add support for IPVS GUE tunnel type This functionality was introduced in Linux 5.2. To view the IPVS setup with ipvsadm requires ipvsadm v1.30 plus commits 2347b504e3ce and c3c2c3c6ae12e3. * Add support for IPVS GUE tunnel checksum option. The kernel functionality is scheduled for Linux 5.3. * Add support for IPVS GRE tunnels. The kernel functionality is scheduled for Linux 5.3. In addition to the ipvsadm patch requirements identified for GUE tunnels, the patch at * Add pure attribute to http_get_check_compare(). GCC was suggesting adding the pure attribute to http_get_check_compare() so let's do so. * Resolve warnings from gcc 9.1.1. * Resolve all outstanding coverity issues. * Fix use of getrandom() in BFD rand_intv(). * When resetting priority of child process, don't change parent's priority Issue 1358 identified that it was the priority of the parent process, rather than the child process, that was being reset. This commit corrects that and resets the priority of the child process. * Add missing bfd_instance vrrp and checker keyword documentation. * Don't send bfd events to vrrp or checker process if no configuration. If there is no vrrp configuration, or no checker configuration, there is no point sending bfd_event notifications to the relevant processes. Actually, since the processes may not be running, sending such notifications can cause the pipes to become full, so it is necessary, as well as desirable, not to write events to the pipes in those circumstances. * Revert use of getrandom() for bfd jitter. This can be called up to 1000 times a second per bfd instance, and so risks emptying the entropy pool. * Use random() rather than rand() in bfd rand_intv(). The rest of keepalived uses random(), so this changes creates more consistency. * Allow bfd discriminator to be an odd number. rand_intv(1, UINT32_MAX) was always returning an even number, since RAND_MAX == UINT32_MAX / 2. This commit sets the lsb of the discriminator to the lsb of the current time in seconds. * Ensure BFD source port in range 49152..65535. RFC5881 requires the source port for BFD packets to be in the above range, but keepalived was allowing the port to be randomly generated by the kernel, and hence could be outside the range. This commit sets the permitted port range to the intersection of [49152, 65535] and the values in /proc/sys/net/ipv4/ip_local_port_range, unless the intersection is too small, in which case it just uses the BFD specified values. keepalived generates a random port number in the required range, and then loops through the range starting from the random port number until it finds one it can bind to. * Resolve coverity resource leak issue 218872. * Resolve coverity Resource leak issue 218875. * Resolve coverity Resource leak issue 218876. * Resolve coverity Unexpected control flow issue 218873. * Change code to avoid coverity String length miscalculation issue 218874 The code was correct, but as coverity points out, strlen(str + 1) is more likely to be an error for strlen(str) + 1, so avoid the use of the former construct. 2019-06-25 Alexandre Cassen * keepalived-2.0.17 * Add support to define CPU affinity for vrrp, checker & bfd processes Created 3 new configurations keywords to set CPU affinity of Keepalived processes : vrrp_cpu_affinity, checker_cpu_affinity & bfd_cpu_affinity This option can be used to force vrrp, checker and bfd processes to run on a restricted CPU set. You can either bind processes to a single CPU or define a set of cpu. In that last case Linux kernel will be restricted to that cpu set during scheduling. Forcing process binding to single CPU can increase performances on heavy loaded box. for example: "vrrp_cpu_affinity 2" will force vrrp process to run on cpu_id 2 "vrrp_cpu_affinity 2 3" will retrict kernel scheduling decision over cpu_id 2 & 3. * correct syntax error when _HAVE_VRRP_VMAC_ && no HAVE_IFLA_LINK_NETNSID. * Stage libmnl and libnftnl4. * Add dynamic download of kernels using scriplets Also added Linux 5.0.0 build. * Example build using EOL kernel from old-releases. * Modify snapcraft.yaml to dynamically source correct kernel versions. * dump processes CPU Affinity while dumping global conf. Add support to dump CPU Affinity for each Keepalived processes where CPU Affinity has been changed by configuration. * Don't enclose /dev/tcp/127.0.0.1/22 in ' chars when running as script RedHat identified a problem with scripts like: vrrp_script { script "' resolves the problem. * Add support for use_ipvlan (use an ipvlan i/f similar to use_vmac) Issue #1170 identified that use_vmac didn't work with systemd-networkd since systemd-networkd was removing IP addresses created by keepalived (and any other application). It was discovered that systemd-networkd did not remove IP addresses from ipvlans. This commit adds support for ipvlans, but to work around the problem, and because it might have other uses. Systemd commit - https://github.com/systemd/systemd/pull/12511 has added configuration options to stop systemd-networkd removing IP addresses added by other applications, but it is not merged yet, and it will be a while before all the distros merge it. * Fix building with ipvlans before IFLA_IPVLAN_FLAGS was defined. * Default IPVLANs to bridge mode We shouldn't change the behaviour if a kernel is upgraded, so default to the original mode supported. * Ensure that -lm linker library flag is always set configure was testing whether it was necessary to add the -lm option, but for some reason gcc adds it itself if -Os is not specified, but does not add it if -Os is specified. Consequently if configure was run without -Os, and make was run with -Os the link failed. The commit ensures that -lm is always used. * Handle checking for -Wl,-z,relro and -Wl,-z,now properly. * Honour CFLAGS, CPPFLAGS, LDLIBS and LDFLAGS settings when configure runs. * Propogate CFLAGS, CPPFLAGS, LDFLAGS and LDLIBS from configure to make files Make sure any settings in CFLAGS etc at the time configure is run are added to the Makefiles, to ensure that the make is run in the same environement that configure is run in. * Use CFLAGS, CPPFLAGS, LDFLAGS and LDLIBS correctly Use the correct variable for the relevant option type, e.g. -llib should be in LDLIBS, not LDFLAGS, and -Ddefn should be in CPPFLAGS not CFLAGS. * Fix non-ipvlan interfaces broken by adding ipvlans. * Check bfd instance name length before copying. * Add lib/container.h to avoid duplicate definition of container_of. * Revisited code to use const declaration where appropriate. * Add STRDUP/STRNDUP functions. * Add FREE_CONST, FREE_CONST_ONLY and REALLOC_CONST. * Change thread_t * to thread_ref_t except in thread handler code Treat the thread reference as a handle, so that the only code that manipulates thread structures is in the scheduler. * Add STRDUPs in check_data. * Add STRDUP in bfp parser code. * -U flags should be included in CPPFLAGS * Update track_process documentation. Issue #1265 requested further clarify regarding the track_process process specification and use of quote marks. * Fix building on Linux 3.13 (required for building snaps) * Ensure 4 extra parameters are set for notify scripts with no shebang. * Streamline functions returning string matching a define. * Make addattr8/16/32/64 and rta_addrattr8/16/32/64 inline functions Since these functions simply call addattr_l/rta_addattr_l, making the functions inline removes the overhead of one function call. * Add genhash option -P to select HTTP 1.1 or 1.0 with Connection: close Max Kellerman (max.kellermann@gmail.com) submitted pull request #1260 to add "Connection: close" to the HTTP header sent by genhash. In order to maintain backwards compatibility, this has been implemented as an option '-P 1.0C'. In addition, '-P 1.1' requests that a version 1.1 header is sent (which includes 'Connection: close'). * Add http_protocol option for HTTP_GET and SSL_GET checkers. To be consistent with commit 2ff56f5 - "Add genhash option -P to select HTTP 1.1 or 1.0 with Connection: close", this commit adds the http_protocol keyword for HTTP_GET and SSL_GET checkers. 'http_protocol 1.0C' adds 'Connection: close' to a 1.0 header, and 'http_protocol 1.1' sends an HTTP/1.1 header, which includes the 'Connection: close' option. * Tidy up the recieve message processing code loops in genhash. * Add genhash -t timeout option. * Simplify thread process in genhash after send HTTP request. * support http status_code group The origin status_code only support one specific code, now we can support http status_code of the same class. That's to say, we can use 1xx to represent 100-199, 2xx means 200-299 ans so on. eg: The configure as follows: url { path /index.html status_code 2xx 3xx } which means we consider all status_code range in [200,399] is ok. Of course the following configure is either 200 or [300,399] is ok. url { path /index.html status_code 2xx 3xx } * Fix compiler warnings introduced in commit c7c23a2 Commit c7c23a2 - "support http status_code group" introduced two compiler warnings, due to isdigit() being undeclared, and a shadows declaration. These warnings are now resolved. * Use standard bit testing and setting functions Commit c7c23a2 - "support http status_code group" added additional bit testing and setting functions, rather than using the already defined ones in bitops.h. This commit also resolves the assumption that longs are 64 bits, and will allow the code to work with longs of any length. The original commit would cause all status codes 100 to 599 to be written when the configuration was dumped, regardless of whether the specific codes were set. This commit now writes the status codes in ranges. Finally, if no status code is configured, it sets the bits for the default status codes (200-299). * Change how http status codes are configured Commit c7c23a2 - "support http status_code group" allowed status codes to be specified as 2xx, meaning 200-299. This commit changes the configuration so that 2xx etc is no longer used, but status code ranges can be specified, e.g. status_code 150 180-189 200-299 503 510-520 * Update documentation for commit c7c23a2. * Fix a memory leak and duplicate free in HTTP_GET checker. * Fix sending SMTP alerts Issue #1275 identified that SMTP alerts were not working. The SMTP alerts were broken by commit 5860cf2 - "Make checker fail if ENETUNREACH returned by connect()", since the SMTP state machine was not updated to handle the addition value in enum connect_result. This commit adds code to handle the additional enum, but also makes the code less sensitive to such changes, and more likely to produce compiler warnings/errors if appropriate updates are not done in the future. * Fix various compilation warnings with certain configure options. * Update location of PID file to match Filesystem Hierarchy Standard v3.0 Issue #1277 identified that PID files should be created in /run rather than /var/run, and that systemd logged a warning if the service file specified PIDFile under /var/run. This commit now makes keepalived use the appropriate directory for PID files as determined by configued (rather than doing its own thing), and configure now uses /run in preference to /var/run. * Stop LVS sync daemon on shutdown The shutdown of the sync daemon was delayed to phase 2 of the shutdown which meant that the controlling VRRP instance could never be in the master state. We now stop the sync daemon in phase 1, when the VRRP instance is transitioned out of master state. * Use -isystem rather than -I for path to kernel headers Using -isystem rather than -I allows the dispensation for some warnings to system headers to apply to the kernel header tree we are specifying. This stops some warnings that would not occur with kernel headers under /usr/include but that were being generated when -I was used (it nevertheless has helped identify two bugs). * Ensure check system headers for definition of NFT_TABLE_MAXNAMELEN Prior to Linux 4.1 NFT_TABLE_MAXNAMELEN was not defined, but we must include linux/netfilter/nf_tables.h before checking whether it is defined or not! * Improved configure testing for * Add warning -Wwrite-strings and resolve new warnings. * Add -Wdouble-promotion and resolve new warnings. * Add -Wformat-signedness and resolve new warnings. * Fix building on Ubuntu 16.04 with --disable-vrrp The addition of including was needed on Ubuntu 16.04, whereas it wasn't necessary on Fedora or Debian. * Explicitly include where print format names are used. * Add more -Wformat-* options and resolve new warnings. * Add -Wframe-larger-than=5120 The largest frame is just under 4200 bytes (which may be more than we want anyway), but adding this warning will at least tell us if a stupidly large frame is created in the future. * Fix spelling of -Wmissing-field-initializers. * Fix definition of PRI_rlim_t generated by configure on 32 bit systems. * Rseolve warning re >=0 comparison for unsigned value. * add min max judge Although even if min > max, the code works well. We better to print the error config to let the user know this. * Ensure correct definition of MAX_ADDR_LEN is used defines MAX_ADDR_LEN as 7, and defines MAX_ADDR_LEN as 32. We need to ensure we have the longer one. * update doc samples of keepalived.conf.status_code. * Fix compiling on Alpine Linux 3.7. * Update list of packages to install on Alpine Linux. * Send GARP/NA message when leaving fault state if using unicast If the master's ARP entry for a backup route has expired and we are using a short advert interval (< 0.5 seconds), then the backup router could timeout receiving adverts before the master sends its next ARP/NDISC message; until it has had a reply to that it cannot send any adverts to the backup router in question. This commit makes a VRRP instance that is using unicast send a GARP/NA when it transitions out of fault state, to ensure that the master (or local router) can send adverts to us immediately. * track_process: handle different threads having different names prctl(PR_SET_NAME) is a per thread property, not a per process property, so when a PROC_EVENT_COMM event is received, we need to check that the tid == pid, so ensure that only the main (initial) thread that COMM changes are considered for. * Fix some log_message for specifiers in track_process.c. * Fix for JSON characters escaping. * Don't attempt to create a macvlan when using an ipvlan netlink_link_add_vmac() detected an interface had been created, and so didn't attempt to create a macvlan, but netlink_link_add_vmac() shouldn't be called in this circumstance. * On reload, report addresses being removed as removed, not thos remaining. * Don't add further iptables entries on reload when using ipsets. * Stop deleting VMAC/IPVLAN interfaces on reload when still needed. * Fix formatting of email To: line. * Improve efficiency of setting up SMTP headers. * Fix segfault when we do not config vsg. * Fix issues reported by coverty (unchecked return value, buffer overrun, Logically dead code, uinitialized var, explicit null dereferenced, ...) * Resolve compiler warning in list_sort(). * genhash: make printssl a static function. * Change strncpy() to strcpy_safe() in smtp_final(). * Convert some snmp list loops to use LIST_FOREACH. * Make inet_stosockaddr() return bool rather than int. * Fix checking for VMAC/IPVLAN no longer used after reload Pull request 1310 identified that there was a problem building keepalived with VLANs but without ipvlans. The code that needed changing was also incorrect so this commit resolves both issues. * Fix false-positive send_instance_notifies calls Issue #1311 identified that duplicate notifies were being sent on a reload, and pull request #1312 provided a fix. Unfortunately other intervening commits stopped the original patch applying, so this updates the original patch. The patch also stops duplicate logging of vrrp instance states on reload when there has been no change. * Set thread parameter value explicitly to 0 when add timer thread It is possible for a function to be called either from a timer thread or an event thread. When an event thread is added, a vlue can be passed which will be passed to the function, but currently there is no way to set the value for a timer thread (a function thread_add_timer_val() can be added when needed), but in order to allow the value to be used with an event thread, it needs to be explicitly set to something when called via a timer thread, so just set it to 0. * Remove VRRP_DISPATCHER definition - it was not used. * Some minor tweaks for the format of keepalived.data. * Make track_process, parser and dump_keywords --debug options. * Change default to not check for EINTR if use signalfd. * Don't send prio 0 adverts for deleted VRRP instance that wasn't master When a VRRP instance ceases to exist following a config reload, we must only send priority 0 adverts if the deleted instance was in master state prior to the reload. * Send notifies when vrrp instance deleted on reload This commit makes notifies be send saying that the instance is in fault state, since that is the closest we have to the instance being deleted (the instance can't run since it is deleted which is quiet similar to being in fault state). * Streamline some HTTP_GET code. * Simplify HTTP_GET epilog parameters Parameters t and c weren't needed, since they can be determined from the method parameter if we add REGISTER_CHECKER_FAILED. * Set checker->has_run for HTTP_GET after failure The behaviour we want after a failure of checking a URL at startup is the same as if all checks had completed, so if there is a failure, just set checker->has_run. * Make http_get url_it point to list element rather than a counter This makes fetching the next URL more efficient. * When we run the initial HTTP_GET check, we don't want any retries It isn't only the first URL that shouldn't have retries, but all of them. This commit implements that. * When an HTTP_GET url check fails, keep checking that URL until success When a URL check has failed, there is no point checking other URLs until we know the one that has failed is working again. The approach now is that the failed URL is checked until it is Ok again, and then all the URLs are checked before the checker is successful. This will reduce the recovery time once the failed URL recovers. * When starting up, don't delay between checking all the URLs When we start up, particularly in alpha mode, we want to check the URLs as quickly as possible, so don't delay by delay_loop between checking each URL, but check them immediately one after the other. * After HTTP_GET URL failure, delay max of delay_loop and delay_before_retry. * After an HTTP_GET failure, check the URLs without any delay This means that recovery will occur as quickly as possible. * Some cosmetic changes to check_ssl.c. * Add option fast_recovery for HTTP_GET. Commits 3027e0c - "When starting up, don't delay between checking all the URLs" and 86e02dd - "After an HTTP_GET failure, check the URLs without any delay" removed the delay between URL checks both at startup and after a URL check failure. This commit makes that options, and it will only do the fast checking if fast_recovery is configured against the checker. * Make set_value() check for missing parameter Pull request #1308 identifed that if set_value() was called when there wasn't a parameter on the command line, keepalived could segfault since NULL was returned (examples were HTTP_GET with an empty path specified, and DNS_CHECK with empty name). This commit modifies set_value() so that keepalived will exit if it is called with no keyword parameter is missing. Uses of set_value() where no parameter did not cause a problem (e.g. where the whole option was optional, such as virtual_host) now check if the parameter is mising and report a configuration error. * Handle vrrp tracked interfaces being down on reload If the base interface of a vmac interface was down on reload, the vrrt instance would not come back up after the base interface came back up. * Don't log error when sending priority 0 advert after interface goes down. * Cosmetic change to address_exist(). * Add information regarding SElinux and keepalived. * Fix overflow status code Under normal circumstances, status_code returns 100-599, but if it is a constructed abnormal reply message, it may be out of the range, resulting in the status_code array out of bounds, and then keepalived segfault. * Ensure HTTP status code is preceeded by a space character. * Fix setting existing macvlan etc base interfaces at startup. * Add further SELinux references. * Resolve implicit declaration of function ‘strdup’ warning. * Allow location of /run dir to be specified to configure The commit adds configure option --with-run-dir=PATH * Fix reloading when interfaces deleted and recreated If have macvlans on a real interface, with vmacs configured on the macvlans and the macvlans are deleted, the vmacs from them are removed from the configuration, the configuration is reloaded, and this is done for more than one macvlan, and then the configuration is reinstated one by one with the configuration being reloaded, keepalived was incorrectly setting some of the vrrp instances to fault state. This commit resolves the issues. 2019-05-03 Alexandre Cassen * keepalived-2.0.16 * Add log_unknown_vrids keyword. Commit 21e6f5f added logging when a VRRP packet was received on an interface and the VRID in the advert was not configured on that interface. Due to valid uses of keepalived having a VRRP instance on an interface, but there being other, independent, VRRP instances with different VRIDs on the same interface, this patch only enables logging of unknown VRIDs if it is specifically configured. * Stop segfault when reload and using -x option. * Fix compilation error found by Travis-CI. * Fix a couple of typos. * Ensure check command line when needed for track process. * Check if comm really changed when get PROC_EVENT_COMM_CHANGE. * Fix debounce delay handling for track_process. * Optimise add_process(). * Remove processes no longer being monitored. * Optimise check_process(). * Ignore process threads for track_process. * Allow matching of process parameters in track_process This additional functionality was requested in issue #1190. * Allow separate delay timers for fork and process exit in track_process. * Add quorum_max for track_process. This allows track_process to go to fault state if more than a specified number of instances of a process are running. In particular it can go to fault state if more than one instance is running, and also if any instance of a process is running. * Add configuring process name. With up to 4 processes running all named keepalived, it can be difficult to know which is which. The commit adds the option to allow process name to be set independantly for each process. * Handle macvlans/macvtaps being moved into different namespace from parent If a macvlan or macvtap interface is moved into a different namespace from its parent, and the interface is in the namespace in which keepalived is running, keepalived is unable to get information about, or configure, the parent interface. In this case, treat the macvlan/macvtap interface as though it doesn't have a parent interface. There are a couple of consequences of this in this situation: 1) If a vrrp instance is configured with use_vmac and its configured interface is such a macvlan/macvtap interface, keepalived cannot ensure that the arp_ignore and arp_filter settings are correct on the parent 2) keepalived cannot check that there a not duplicate VRIDs being used on the interface. * Typo writing word error fix. * Add vrrp instance priority change notifications on FIFOs only. Issue #1213 requested notification of vrrp instance priority changes, and this commit implements that with new FIFO messages: INSTANCE "VI_0" MASTER_PRIORITY 220 INSTANCE "VI_0" BACKUP_PRIORITY 254 This has been implemented via notify FIFOs only, since the order of processing of scripts is indeterminate if events happen quickly in succession, potentially causing the last processed priority by a script not to be the lastest priority, and using SMTP notification would be ridiculous. * Allow user and group ownership of FIFOs to be configured. * Remove extraneous debugging message from process_name commit Commit 4ad6d11 - "Add configuring process name" accidentally left a debugging log message in the code. This commit removes it. * Fix FREE error if tracked process has no parameters. * Fix track processes when reloading. * Fix route add/delete on reload if only change via address If a virtual_iproute src 100.100.100.100 2.2.2.2/32 via 100.100.100.2 dev eth0 is changed to src 100.100.100.100 2.2.2.2/32 via 100.100.100.1 dev eth0 on a reload the route didn't get updated. The reason is that the via address wasn't used in the comparison of routes, so keepalived didn't detect that it had changed. * Define TASK_COMM_LEN rather than use numbers in code. * Fix promote_secondaries. * Add snmpd.service to keepalived.service if SNMP enabled. * Add issue templates for github. * Make utils.c function parameters const where appropriate. * Add missing info to check process dump file. * Make ipvs_talk() error message more meaningful The error message used to just output the IPVS command number, now the name of the command is reported too. * Make more use of LIST_FOREACH in ipwrapper.c. * Change VS_ISEQ etc to be functions and correct them. * Resolve removing virtual servers in virtual server groups after reloading. * Update NOTE_vrrp_vmac.txt re sysctl settings. * Ignore base interfaces of macvlans if in a different namespace. * Don't lose sin_addr_l and sin6_addr_l lists from interface when recreate Issue #1232 identified that keepalived segfaulted when an interface was recreated. This commit resolves the problem of the address lists being lost. * Fix commit 128bfe6 for pre v4.0 kernels Commit 128bfe6 - "Ignore base interfaces of macvlans if in a different namespace" added using IFLA_LINK_NETNSID to detect if the parent of an interface was in a different namespace. Unfortunately that was only introduced in Linux v4.0, so don't attempt to use it if it is not defined. For kernels older than v4.0 if a macvlan interface's parent is in another network namespace, but the ifindex of the parent interface also exists in the namespace in which keepalived is running, then keepalived will believe the parent of the macvlan is the wrong interface. * Fix commit 3207f5c - IFLA_LINK_NETNSID is not #define'd This fixes commit 3207f5c - "Fix commit 128bfe6 for pre v4.0 kernels". A configure test is needed to check for IFLA_LINK_NETNSID. * Further fixes/improvements for MACVLAN parents in different namespaces. * allow to set zero weight for real server. * Add comments re needing to enable protocol 112 in an AWS security group. * Check if base i/f of a residual macvlan is in correct namespace. * Stop segfault if using DBus and have invalid VRRP configuration. If a VRRP instance was removed by vrrp_complete_init() it was causing a segfault in the DBus code. The commit moves the initialisation of DBus until after the validity of the VRRP instances has been checked. * Handle DBus process properly when reloading. DBus may change from being enabled to disabled or vice versa and the code didn't handle that. * Close DBus pipes when stop using DBus. * Add some more LIST_FOREACH to DBus code. * Move a g_free() to after last use of the freed string in vrrp_dbus. * Fix error in man page. * Handle network namespace name properly when reloading. * Don't call g_hash_table_remove() when using g_hash_table_foreach_remove() g_hash_table_foreach_remove() removes each object from the hash table, so calling g_hash_table_remove() as well made it not work properly. * Resolve various aspects of reloading when also using DBus. 1. Add ability for DBus to be enabled and disabled at reload 2. Correctly handle vrrp_instance name change for matching interface/ family/VRID. 3. Correct handling of interface/family/VRID change for a vrrp_instance with the same name. * Resolve segfault when a vrrp_instance has no interface specified. * Fix sending priority 0 adverts after reload for deleted vrrp instances. During a reload, vrrp_dispatcher_release() was called prior to reloading the configuration, and it closed all the vrrp send/receive sockets. However it isn't until after the reload that it is known which vrrp instances no longer exist, and clear_diff_vrrp() attempted to send 0 priority adverts for those instances. Since the sockets had already been closed, the adverts could not be sent. Worse, the socket_t structures had been released, but the released memory was accessed in attempting to send the adverts. This commit delays calling vrrp_dispatcher_release() until after the new configuration has been reloaded, and it sends 0 priority adverts before all the old sockets are closed. Following this new sockets are opened. It would be possible to make the code more efficient and retain the sockets that still need to be used, rather than closing them and opening new ones, but that is for another commit. * Update some comments in vrrp_snmp.c. * Use structure initialisation to clear struct, rather than memset. * Fix logging if receive EPOLLHUP, EPOLLERR and add for EPOLLRDHUP. * Add support for network timestamp debugging. * Check return code from recvfrom() before other values for track_process. * Use IPV6_RECVPKTINFO rather than IPV6_RECVHOPLIMIT when check multicast. * Ensure virtual servers are properly removed when reloading. Pull request #1246 provided a patch to resolve the issue of virtual servers in a virtual server group that are deleted from the virtual server group on a reload weren't being removed from the IPVS configuration. However, the patch didn't quite work with the current HEAD of the master branch. This commit incorporates that patch provided and makes the necessary adjustments for it to work correctly. * Cosmetic changes to IPVS code. * Make clear the IPv6 instances use VRRP version 3. * Delete redundant code. * Update comments in vrrp_nftables.c. * Update for gcc v9 Detect if -Wchkp is no longer supported, and fix a -Wstrict-overflow warning in write_backtrace(). * Add additional compiler warnings available in gcc verion 9. 2019-04-04 Alexandre Cassen * keepalived-2.0.15 * Fix uninitialised variable. * Fix rpmbuild on CentOS7, and rely on auto-requires. * Add option to flush lvs on shutdown. Currently all known virtual servers and their real servers are removed one at a time at shutdown. With large configurations on a busy system, this can take some time. Add an option just like the existing 'lvs_flush' which operates on shutdown. Typical environments with a single keepalived instance can take advantage of this option to achieve a faster shutdown or restart cycle. * Make alpha mode checkers on new real servers start down on reload. Patch #1180 identified that new real servers with alpha mode checkers were being added online immediately, and if the checker then failed were being removed. This commit makes real servers that didn't exist before the reload start in down state if they have alpha mode checkers. * Remove duplicate config dump entry. * Make new real servers at reload start down if have alpha mode checkers. * Close checker and smtp_alert sockets on reload. Issue #1177 identified that sockets were being left open (lost) after a reload. It transpired that these were sockets opened by TCP_CHECK, HTTP_GET, SSL_GET, DNS_CHECK and SMTP_CHECK checkers, and by smtp_alerts in the process of being sent. This commit adds an extra parameter to thread_add_read() and thread_add_write() to allow indicating that the scheduler should close the socket when destroying threads. * Send vrrp group backup notifies at startup. * Make inhibit_on_failure be inherited by real server from virtual server. * Allow real and sorry servers to be configured with port 0 This is to maintain backwards compatibility with keepalived prior to commit d87f07c - "Ensure always check return from inet_stosockaddr when parsing config". The proper way to configure this is to omit the port, which requires the next commit. * Don't setup IPVS config with real and virtual servers ports different. If the real server is using DR or TUN, the port of the real server must be the same as the port of the virtual server. This commit uses the virtual server port for the real server when configuring IPVS. * Log warnings if real server and virtual server ports don't match This commit adds logging warnings if virtual and real server ports, when using TUN or DR, don't match. It also sets the real server ports to be the same as the virtual server ports. Although listing the IPVS configuration with ipvsadm will look different, the kernel ignored the port of a real server when using DR or TUN, so the behaviour isn't changed, but when looking at the configuration it now shows what is actually happening. * Fix warning when protocol specified for virtual server with fwmark. * Add log message that nb_get_retry is deprecated. * Fix whitespace in configure.ac. * Fix configure error when systemd not installed configure was trying to execute pkg-config --variable=systemdsystemunitdir systemd even if systemd was not available. This commit makes configure only execute the above if it has determined that systemd is the correct init package to use. * Correct references to RFC6527 (VRRPv3 SNMP RFC). * nsure checker->has_run is always set once a checker has run. * Fix some indentation in configure.ac. * Update fopen_safe() to open temporary file in destination directory rename() in fopen_safe() was failing if the file being created was not on the same filesystem as /tmp. * Add ${_RANDOM} configuration keyword. It might seem strange to introduce random elements to configuration files, but it can be useful for testing. * Fix using ~SEQ() in multiline configuration definitions. * Make blank lines terminate a multiline definition. * Minor updates for lvs_flush_on_stop. * Add option to skip deleting real servers on shutdown or reload If a virtual server is removed, the kernel will remove its real servers, so keepalived doesn't explicitly need to do so. The lvs_flush_onstop option removes all LVS configuration, whereas this new option will only remove the virtual servers managed by keepalived. * Correct error message re checker_log_all_failures. * Fix syntax error in configure.ac. * Fix track_process initialisation for processes with PIDs starting 9. * Remove debugging log message. * Remove inappropriate function const attributes They were causing iptables/ipsets not to be initialised. * Stop warning: function might be candidate for attribute ‘const’ Depending on what configure options are selected, gcc can output the above warning for initialise_debug_options(). This commit ensures that the warning is not produced. * Enable strict-config-checks option in keepalived.spec RPM file. * vrrp: relax attribute 'const' warning at iptables helpers. * Propagate libm to KA_LIBS. * Fix building on Alpine Linux. Alpine (musl) doesn't have a definition of __GNU_PREREQ, so create a dummy definition. 2019-03-24 Alexandre Cassen * keepalived-2.0.14 * Add compiler warning -Wfloat-conversion and fix new warnings. It was discovered that passing 0.000001 as a parameter specified as uint32_t to a function did not generate any warning of type mismatch, or loss of precision. This commit adds -Wfloat-conversion and fixes 3 instances of new warnings that were generated. * For non systemd enviroment, it occurs syntax error 'fi'. To avoid syntax error, modify keepalived.spec.in. * When uninstall keepalived with init upstart, stop keepalived process. * Fix type re LOG_INGO should be LOG_INFO * 6git stash --cached. The code was actualy in a #ifdef INCLUDE_UNUSED_CODE block, and so isn't currently compiled. * Register missing thread function for thread debugging. * Fix reutrn value of notify_script_compare misusing issue. * Fix typo in keepalived.conf man page re BFD min_rx. * Fix segfault when bfd process reloads config. Issue #1145 reported the bdf process was segfaulting when reloading. The bfd process was freeing and allocating a new thread_master_t when reloading, which doesn't work. This commit changes the bfd process to clean and reinitialise the thread_master_t. * Fix segfault in handle_proc_ev(). On Linux 3.10 the ack bit can be set in a connector message, and the CPU number is set to UINT32_MAX. This commit skips acks, and also checks that CPU number is within range of the number of CPUs on the system. * Fix OpenSSL init failure with OpenSSL v1.1.1. OpenSSL v1.1.1, but not v1.1.0h or v1.1.1b failed in SSL_CTX_new() if OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG) had previously been called. This commit doesn't call OPENSSL_init_crypto() if doing so causes SSL_CTX_new() to fail. * Remove all references to libnfnetlink. Commit 2899da6 (Stop using linbl for mcast group membership and setting rx buf sizes) stopped using libnfnetlink, but INSTALL and keepalived.spec.in were not updated accordingly. * Fix genhash re OPENSSL_init_crypto bug and improve configure.ac. Commit fe6d6ac (Fix OpenSSL init failure with OpenSSL v1.1.1) didn't update the identical code in genhash/ssl.c. Also, an improvement for the test in configure.ac was suggested. * Fix log output when real server removed. FMT_VS() and FMT_RS() both call inet_sockaddrtotrio which uses a static buffer to return the formatted string, but since FMT_VS(), wheich simply calls format_vs() copies the returned string to its own static buffer, if FMT_VS() was called before FMT_RS() then the returned strings from both could be used. The problem occurs when both FMT_VS() and FMT_RS() are used as parameters to log_message() (or printf etc). It appeared to work fine on x86_64, but was writing the same IP address for both the real server and virtual server on ARM architectures. This is due to the compiler evaluating parameters to the log_message() function call in a different order on the different architectures. This commit adds inet_sockaddrtotrio_r() which allows the output to be in a buffer specified by the caller, and so FMT_VS() and FMT_RS() can now be called in either order without one overwriting a buffer used by the other. * Streamline some string formatting with FMT_RS() and FMR_VS(). Following commit 9fe353d (Fix log output when real server removed) some code can be streamlined now that the order of calling FMT_VS() and FMT_RS() does not matter. * Replace FMT_HTTP_RS(), FMT_TCP_RS() and FMT_DNS_RS() with FMT_CHK(). They were all simply defined to be FMT_CHK() so just replace them with that. This made it much simpler to find all used of FMT_CHK(). * Fix building with gcc 4.4.7 (Centos 6.5). gcc v4.4.7 doesn't support -Wfloat-conversion, so check for it at configure time. * Add dumping checker config/status when receive SIGUSR1. * Don't put alpha mode checkers into failed state at reload If a new checker is added at a reload, unless the real server aleady has failed checkers, then ignore the alpha mode of the checker. This means that the real server, if up, won't be taken down and then brought back up again almost straight away. If the real server already has failed checkers, then setting an alpha mode checker down initially won't take down the real server, so we can allow the alpha mode setting to apply. * Handle alpha mode checkers initial failure at startup better. * Fix compile failure discovered by Travis-CI. * Fix calling syslog when not using signalfd(). Pull request #1149 identified that syslog is AS-Unsafe (see signal-safety man page), and that therefore signals should be blocked when calling it. This commit blocks signals when calling syslog()/vsyslog() when signalfd() is not being used. * Rationalise function attributes. * Fix enable-optimise configure option. * Use AS_HELP_STRING for all options in configure.ac. * Streamline genhash -h option. * Make genhash -v version match keepalived. * Fix config check of virtual server quorum against weights of real servers. * Fix some configure tested checks for OPENSSL_init_crypto. * Add infrastructure for adding additional compiler warnings. * Add standard and extra compiler warnings. * Add and resolve missing-declarations and missing-prototypes warnings Approximately 16 additional functions are now declared static. * Add and resolve old-style-definitions warnings * Add and resolve redundant-decls warnings * Add and resolve jump-misses-init warnings * Add and resolve shadow warnings * Add and resolve unsuffixed-float-constants warnings * Add and resolve suggest-attribute=const warnings * Add and resolve suggest-attribute=format warnings * Add and resolve suggest-attribute=malloc warnings * Add and resolve suggest-attribute=noreturn warnings * Add and resolve suggest-attribute=pure warnings * Add and resolve unused-macros warnings * Add and resolve null-dereference warnings * Add and resolve float-equal warnings * Add and resolve stack-protector warnings * Add and resolve strict-overflow=4 warnings * Add and resolve pointer-arith warnings This particularly includes adding a number of bytes to a void *. * Add and resolve cast-qual warnings * Resolve additional warnings identified on Centos 6.5/gcc 4.4.7 * Remove static from zalloc() * Fix some compiler warnings on Ubuntu Xenial, and add comments re others. * Rename LIST parameters to lst in list_head.h to avoid upper case. * Fix real server checkers moving from failed to OK on reload. * add rs judgement in migrate_checkers. * Detect connection failure in genhash and exit rather than loop. * Add another function pure attribute. * Fix sending notifies for vrrp instances at startup when in sync group Issue #1155 idenfified that notify scripts for vrrp instance transition to backup state when keepalived started up were not being sent if the vrrp instance was in a sync group. It was also the case that SNMP traps, SMTP alerts and FIFO notifies were not being sent either. This commit make keepalived send the initial notifies when the vrrp instance is in a sync group. * Fix building keepalived RPM on Fedora 26. For some reason -fPIC is needed when testing for the presence of setns(). * Add vrrp_startup_delay configuration option. Some systems that start keepalived at boot time need to delay the startup of the vrrp instances, due to network interfaces taking time to properly come up. This commit adds a global configuration option vrrp_startup_delay that delays the vrrp instances starting up, for the specified number of seconds. * Handle checkers properly when reload immediately after startup. * Streamline some of the SMTP checker code. * Create separate checker for each host in SMTP_CHECK block Having multiple host entries in an SMTP_CHECK block is deprecated. This commit streamlines the SMTP_CHECK code by creating a separate SMTP checker for each host declared in the SMTP_CHECK block, so that apart from parsing the configuration, the code no longer handles multiple hosts per checker. The support for parsing configuration with multiple hosts is only enabled if WITH_HOST_ENTRIES is defined in check_smtp.c. It is currently enabled, but when support for multiple hosts in the SMTP_CHECK block is finally removed, it will simply be a matter of deleting all code in the WITH_HOST_ENTRIES conditional blocks. * Make checker fail if ENETUNREACH returned by connect(). The connect() call can return some immediate errors such as ENETUNREACH. These were not being treated as a failure of the checker, since the code used to assume that any non success return by connect() meant that the connection was in progress. keepalived will now treat ENETUNREACH, EHOSTUNREACH, ECONNREFUSED, EHOSTDOWN, ENETDOWN, ECONNRESET, ECONNABORTED, ETIMEDOUT, when returned by connect(), as meaning that the checker has failed. * Don't set SO_LINGER with a timeout of 0 SO_LINGER with a timeout of 0 causes a TCP connection to be reset rather than cleanly closed. Instead of specifying a timeout of 0, use 5 seconds, so that there is an orderly shutdown of the TCP connection, but the close socket doesn't remain in TIMED_WAIT state for more than a short time. * nftables: fix build with kernel lower than 4.1. * Remove dead code & cosmectics. Remove code marked as UNUSED where things simply go nowhere even if define is set. We keep for the moment UNUSED code related to debug helpers used during coding process. 2019-02-19 Alexandre Cassen * keepalived-2.0.13 * Add BFD build option to keepalived.spec rpm file Issue #1114 identified that the keepalived.spec file was not being generated to build BFD support even if keepalived had been configured to support it. * Copy tarball to rpmbuild/SOURCES when building in place It seems that even when building in place, rpmbuild expects the tarball to be in the rpmbuild/SOURCES directory. * Fix configure check for __always_inline * Handle interface MAC addresses changing When an interface is added to a bond interface, if it is the first interface added, the MAC address of the bond interface is changed to the MAC address of the added interface. When subsequent interfaces are added, their MAC addresses are changed to that of the bond interface. Issue #1112 identified that if a bond interface is deleted and recreated, the gratuitous ARPs were sent with the wrong source MAC address. This commit now updates interface MAC addresses from the netlink RTM_NEWLINK messages, so that the correct MAC address is always used. * Minor tidying up of opening gratuitous ARP socket. * Streamline setting SOCK_NONBLOCK on vrrp sockets. * Use netlink reported hardware address length for unsolicited NAs ETH_ALEN is correct for Ethernet type interaces, but is not right for Infiniband interfaces. * Minor tidying up of opening gratuitous NA socket. * Make gratuitous ARP/NA sockets non blocking keepalived shouldn't block when sending gratutious ARP/NA messages. It is better to lose the messages than for keepalived to block, so set the sockets non blocking. * Use netlink provided broadcast address for gratuitous ARP If an interface has a non-standard broadcast address, we should honour it. * Fix building on pre 3.10 kernels re track_process Issue #1119 reported that keepalived wouldn't build on CentOS 6. Various PROC_EVENT_* declarations were assumed to exist, some of which were not introduced until Linux v3.10. Most of them are not needed, but PROC_EVENT_COMM is used by the track_process code. This commit now checks for the existence of the PROC_EVENT_* declarations, but since keepalived uses PROC_EVENT_COMM, track_process is not supported prior to Linux v3.2. * Make track_process work prior to Linux 3.2, but with limitations Prior to Linux 3.2 the PROC_EVENT_COMM event did not exist, which means that keepalived is unable to detect changes to process name (/proc/PID/comm) prior to Linux 3.2. most processes do not change their process name, and so using track_process prior to Linux 3.2 is safe so long as the monitored processes are known not to change their process name. * Stop configure failing when nftables is not supported. * Streamline socket use with linkbeat. Previously the socket used for ioctls was opened and closed twice per poll if using MII or ETHTOOL polling, and once per poll if using ioctl polling. This commit opens the socket once at startup, uses that socket for all linkbeat polls, and closes it on termination. * Enable linkbeat polling to work with dynamic interfaces. * Add linkbeat_interfaces configuration block It was not possible to indicate that an interface that wasn't used as the interface of a vrrp instance, but was used either as a track interface, or for virtual/static ip addresses or routes should use linkbeat. This commit adds that capability. * Add ability to specify linkbeat type in linkbeat_interfaces block. * Add --disable-linkbeat configure option Does anyone use linkbeat anymore? This commit enables keepalived to be build without the linkbeat code. * Don't remove link local IPv6 address from VMAC that isn't keepalived's If IFLA_INET6_ADDR_GEN_MODE isn't supported and a macvlan interface already had a (non-default) link local addresss and the link local address that matched the interface's MAC address was added, keepalived was removing it as soon as it was added. This commit stop keepalived removing the address when we shouldn't. * Set configure init type correctly in keepalived.spec file. * Fix handling of VMACs with multiple reloads If a configuration is loaded that has a VRRP instance using a VMAC, then the configuration is updated to remove that VRRP instance and keepalived reloads its configuration, then the configuration is updated again to reinstate the VRRP instance and the configuration is again reloaded, keepalived thought the VMAC interface still existed, whereas it was deleted following the first reload. This commit ensures that keepalived properly detects whether an interface exists following a reload. * Remember more than one interface local address per interface Keepalived needs a local address for each interface it sends adverts on. If the address keepalived is using is deleted and another address is configured on the interface, then keepalived should start using that address. To do this, a list of configured address on each interfaces needs to be maintained. * Don't consider VIPs as local addresses when restart after crash Keepalived maintains a list of addresses per interface that can be used as source adddresses for adverts. To build the list, keepalived reads the addresses configured on interfaces when it starts. However, if keepalived crashed it will have left VIPs configured on interfaces, and we don't want to use them as advert source addresses. This commit makes keepalived compare the addresses on interfaces to VIPs, and ignores any addresses that are VIPs. * Fix removing left over VIPs at startup. * Use read_timer() when parsing config where appropriate. * Allow fractional warmup, delay_loop and delay_before_retry for checkers To shorten the real server monitoring interval, make it possible to specify decimal value for following items: warmup delay_loop delay_before_retry * Update connect_timeout configuration options Based on the patch submitted by tamu.0.0.tamu@gmail.com this patch allows setting the connect_timeout to a resolution of micro-seconds. The patch also adds the ability to set a default value at the virtual server and real server levels. * Fix unused variable warning when building only with RFC compliant SNMP. * It enable to set zero value as mintime for delay_loop and connect_timeout. * Add option not to check for EINTR if using signalfd() If keepalived is using signalfd(), there are no asynchronous signal handlers, and therefore EINTR cannot be returned. Currently the check for EINTR is enabled by default, and configure option --disable-eintr-debug disables the check, while --enable-eintr-debug enables writing log entries if EINTR is returned. Once sufficient testing has been performed, the default will be changed not to test for EINTR if signalfd() is supported. * Make checking for EAGAIN/EWOULDBLOCK consistent The code in some places checked errno for EAGAIN and EWOULDBLOCK and in other places only checked EAGAIN. On Linux EAGAIN == EWOULDBLOCK, so the check is not necessary, but EAGAIN is not guaranteed to be the same value as EWOULDBLOCK, so define check_EAGAIN that only checks EAGAIN if they are the same value, but checks both if they are different. * Ensure default connection timeout for smtp checker hosts set. * Set default connection timeout if no smtp check host specified. * Fix min timer value, zero to 0.000001Sec. * Add fixing min time for vs_co_timeout_handler() and rs_co_timeout_handler(). * Fix parameter of read_timer(), it treat Mintime and Maxtime as microseconds. * vrrp: vrrp_dispatcher_read() performance extension We took time with Quentin to simulate and rework this code. We introduced 2 imbricated while loop: (1) First one is catching recvfrom EINTR (this code trig only on kernel older than 2.6.22 where signalfd was firstly introduced). Newer kernel will immediately break the loop (hey guys: if you are running older than 2.6.22 it is worth considering upgrading). (2) Second loop will continue reading from socket until same VRID advert has been received during the same cycle. After simulating, it appears that during contention with a lot of VRRP instances (around 1500), this design is needed to relax socket recvq from growing. This can be viewed as a Poll-Mode activation during contention and fallback to regular I/O MUX during normal operations. This loop breaks immediately and re-submit opration to I/O MUX when there is no more to be read. * Fix conversion from long for double in read_timer(). * Remove variable timer of unsigned long cast in read_timer(). When Double type variable timer is cast to long type, it's scale falls. 2019-01-26 Alexandre Cassen * keepalived-2.0.12 * Documentation related. Remove keepalived.conf.SYNOPSIS content to make a pointer to manpage. Update README manifest to reflect actual Keepalived goal and features. * Improve error message if process events connector not enabled in kernel. * Add option to disable track-process functionality Issue #1099 reported that their kernel did not support the proc events connector, and it would therefore be helpful to have an option to build keepalived without the track-process functionality. This commit adds the --disable-track-process configure option. * Fix vrrp instances going to fault state when have virtual routes If an interface going down caused a vrrp instance to go to fault state, and the vrrp instance also had virtual routes, the state of the vrrp instance would be set to backup when the deletion of the virtual route was detected. This commit ensures that the vrrp instance stays in fault state until the interface is brought up again. * Remove Red Hat Linux 9 and RH Enterprise Linux 3 from spec file. Red Hat Linux 9 and Red Hat Enterprise Linux 3 are both based on Linux 2.4, which is no longer supported by keepalived. The options in the spec file for Reh Hat Linux 9 have twice caused people to specify wrong options to configure when trying to build keepalived, so the options are removed to i) avoid confusion and ii) they are not longer relevant. * Add global option vrrp_min_garp. By default keepalived sends 5 gratuitous ARP/NA messages after transitioning to master, and 5 more 5 seconds later. This isn't necessary with modern switches, and so if the vrrp_min_garp option is set, only one gratuitious ARP/NA message is sent after transition to master, and no repeat messages are sent 4 seconds later. * Standardise definition of _INCLUDE_UNUSED_CODE_ * Remove out of date comment re VRRP over IPv6. * Correct typo in keepalived.conf.5. * Directly use structure sizes for packet header lengths. * vrrp_state_fault_rx() is not used. Wrap the function in conditional compilation so it is not compiled * Convert so list loops to use LIST_FOREACH. * Don't recalculate vrrp packet header address. vrrp_get_header() calculates the address of the vrrp header in a received packet, but it was being recalculated in vrrp_in_chk(). This commit passes the already calculated address to vrrp_in_chk(). * Ensure a received packet has an AH header if and only if AH auth. Ensure that a received packet has an AH header if we expect AH authentication, and doesn't have an AH header if we don't expect AH authentication. * Ensure all protocol headers received before return pointer to vrrp header vrrp_get_header() returns a pointer to the vrrp header, but it now returns NULL if insufficient data has been received to include all the (IP, possibly AH, and VRRP) headers (this does not include the VIPs in the VRRP packet). This means that when a pointer to the VRRP header is returned, all fields in all protocol headers can safely be accessed. * Add check of received IPv6 hop count in multicast adverts The VRRP RFC requires that IPv6 hop count MUST be checked to be 255, just as the TTL for IPv6 must be 255. Previously that wasn't being checked, since IPv6 raw sockets don't provide access to the IPv6 header. Using recvmsg() rather than recvfrom(), and setting socket option IPV6_RECVHOPLIMIT allows keepalived to receive the hop count as ancillary data, and that can now be checked. * Improve reading from vrrp receive sockets. Previously no check was made of the return value from recvfrom()/ recvmsg(). This meant than an error could occur (e.g. EINTR), or no data might be returned, and keepalived would still attempt to process the receive buffer as though data had been received. * Enhance and streamline checking of validity of received VRRP packet This includes checking that a packet is multicast, unless unicast is expected in which case it is checked for unicast, ensuring that if AH authentication is used, the next header protocol is VRRP. The sequence of some checks is revised to ensure that the fields being checked are valid to be accessed prior to accessing them, e.g. check that the packet is VRRP version 2 before checking the authentication. * Stop clearing receive buffer before receiving VRRP packets. This is no longer necessary now that the appropriate checks are made of the return status of recvmsg(), and also that the checks of received packet length and packet headers now do all necessary checks. * Add compile time checks for IPV6_RECVHOPLIMIT/IPV6_RECVPKTINFO support. * Update keepalived.spec.in build-requires. The kernel package required for building keepalived is kernel-headers not kernel-devel. Also, it is superfluous to have package kernel in the build-requires! * Add missing file (build.setup) to tarball. * Fix calculating print format to rlim_t in configure.ac. * Fix compiler warnings on 32 bit systems re HASH_UPDATE. Removing all the casts stopped the warnings. * Use PRI_rlim_t when printing rlim_t types. * Use %zd/%zu for ssize_t/size_t to avoid warnings on 32 bit systems. * Fix some space/tab formatting. * Stop declaring some timer definitions unsigned to stop compiler warnings. TIMER_HZ, TIMER_CENTI_HZ, NSEC_PER_SEC were causing some compiler warnings on some systems due to being defined with a 'U' unsigned suffix. Removing the unsigned specifier stopped the compiler warnings. * Fix compiler warning due to incorrect format specifier. An int64_t should use % PRIi64 and not %ld * Stop an uninitialized variable compiler warning. * Fix MEM_CHECK debugging on processors without unaligned memory access. * Don't attempt to use unopened socket for getting ipset version. * Tidy up an error message. * vrrp: make vrrp_dispatcher_read() async while catching error. During investigations we decided to update previous patch to resubmit into I/O MUX on read error. It will make read procedure I/O MUX freindly by removing potential sync operation potentially leading to a global I/O MUX desync. We aggreed, the situation is really and very exceptionnal but could happen. * vrrp: vrrp_arp_thread split. Split the function for maintainability purpose. 2019-01-06 Alexandre Cassen * keepalived-2.0.11 * Fix segfault while shutting down when SNMP activity occurs. Issue #1061 identified that keepalived could segfault when it shut down. It appears that this was caused by data being received on the file descriptors that the snmp agent requests keepalived to monitor with epoll(). Since the read threads weren't being processed during a shutdown, the first time an snmp fd was ready, keepalived discarded the read thread. The second time that fd became ready there was no thread to handle the fd, and, since the assert() statement was not compiled in, non existant data was queued to the thread ready queue. This commit changes the assert() calls to continue, so that non existant data is no longer queued to the thread ready queue. * While shutting down, continue to handle snmp agent fds. Since we don't shutdown the snmp connection until the very end of the shutdown process (we need to be able to send snmp traps), we should continue to handle the snmp fds on behalf of the snmp agent while shutting down. * Ensure snmp agent is in correct state when initialising/closing Make sure the snmp agent is not already initialised before initialising it, and make sure it has been initialised before closing it. * Disable asserts in bfd code by default and add --enable-asserts Asserts were enabled by default in the bfd code, which shouldn't be the case. Add --enable-asserts configure option so that the asserts tests can be enabled while debugging. * Remove debugging log message accidently left in. * Update receive buffers when interface is created. The receive buffer size used by keepalived is based on the largest MTU of any interface that keepalived uses. If dynamic interfaces are being used and an interface is created after keepalived has started, the MTU of the new interface may be larger than the previous largest, so the receive buffer may need to be increased in size. Further, if vrrp_rx_bufs_policy is MTU, then the kernel receive buffers on the receive socket may need to be increased. * Handle MTU sizes being changed. Issue #1068 identified that the MTU size wasn't being updated in keepalived if it changed. This commit now updates the MTU size and adjusts receive buffer sizes accordingly. * Fix syntax error in configure.ac. * Fix double free when global data smtp_helo_name copied from local_name Issue #1071 identified a double free fault. It occurred when smtp_helo_name was not set, in which case it was set to point to the same malloc'd memory as local_name. At termination keepalived freed both local_name and smtp_helo_name. If keepalived needs to use local_name for smtp_helo_name it now malloc's additional memory to copy the string into. * Rename TIMER_MAX to TIMER_MAXIMUM. ulibC defines TIMER_MAX, so to avoid naming conflict rename it. This issue was reported by Paul Gildea who also provided the patch. * Fix segfault when smtp alerts configured. * First working version of nftables. * Restructed code around how iptables/nftables are called This commit also allows building keepalived without iptables support, thereby allowing only nftables support. Adding any other mechanism to handle no_accept mode, i.e. blocking receiving and sending to/from VIPs should be added to vrrp_firewall.c, in a similar way to how nftables/iptables are used. * Update doc files re nftables. * Make nftables handle dont_track_primary appropriately. * Fix config reload with nftables. * Set base chain priorities from configuration. * Use iptables by default if neither iptables or nftables configured. But if the build of keepalived does not include iptables, then use nftables default. * Stop dumping keywords - left turned on after debugging. * Make umask configuration apply to created file. * Add libmnl and libnftnl to travis file. * Fix compilation failure when NFTNL_EXPR_LOOKUP_FLAGS not defined. * Fix compilation failure when build with nftables but without iptables. * Fix order of include files in configure COLLISION test. Since Linux 4.4.11 (commit 1575c09) including linux/if.h after net/if.h works, whereas until glibc fix their headers including net/if.h after linux/if.h causes compiler redefinition errors. Unfortunately the test for the collision was done the wrong way round, as identified in issue #1079. The patch included in the issue report corrects the order of inclusion of the header files. What we should do is ensure that glibc header files are included before Linux header files, so that at least if kernel headers from 4.4.11 onwards are used, the conflict will not occur. * Set CLOEXEC on netlink sockets. * Correct error message for invalid route metric. * Add track_process for vrrp to monitor if another process is running. Configurations frequently include a track_script to check that a process is running, often haproxy or nginx. Using any of pgrep, pkill, killall, pidof, etc, has an overhead of reading all /proc/[1-9]*/status and/or /proc/[1-9]*/cmdline files. In particular reading the cmdline files has a significant overhead on a system that is swapping, since the cmdline files provide access to part of the address space of each process, which may need to be fetched from the swap space. This commit reads the /proc/[1-9]*/stat and/or the /proc/[1-9]*/cmdline files only when keepalived starts, and after that uses the process events connector to track process creation and termination. keepalived will ignore zombie processes, whereas pgrep etc include them. A minimum number of instances of a process can be specified, and also a delay so that if a process is restarted, it won't cause monitoring vrrp instances to immediately transition to fault state but to wait the configured time and it the monitored process starts again it won't transition to fault state. There are potential difficulties with the process event connector if a large number of process events occur very rapidly, since there can be a receive buffer overrun on the netlink socket. This code will detect that happening, increase the receive buffer size, and reread the processes from /proc. * Add missing #include to track_process.c. * Fix number of elements of fd_set read for snmp select info. * Remove thread_event_t when EPOLL_CTL_DEL fails. If snmpd closes a file descriptor, when keepalived attempts to unregister the fd from epoll an error is returned. However, we still need to remove the thread_event_t from the io_events rbtree. * Fix connection to snmpd after it has to reconnect. Issue #1080 identified that keepalived wasn't handling a connection failure and reconnect to snmpd properly. The problem was created when the change from select() to epoll() was made. This commit makes keepalived unregister and reregister the snmp file descriptors after snmpd reconnects. * Fix retry count for SMTP_CHECK checker. The checker was doing one too few retries. * Make healthchecker failure reporting consistent Some healthcheckers were reporting all failures, and others only when the retries expired. This commit by default makes the checkers only report failure when the retries expire, unless the global keyword checker_log_all_failures or log_all_failures on the specific checker is configured. * After reload, reinitialise current track processes state. * Remove unused variable in track_process.c. * Add configure checks re --with-kernel-dir. * Convert remaining select() to epoll_wait(). keepalived was using select() for handling the termination of child processes, but the main scheduling loop now uses epoll_wait(), so convert the select() to epoll_wait() from consistency. * Stop keepalived leaving zombie child processes. keepalived wasn't reaping the termination of its child processes, so this commit adds waitpid() calls once it knows the processes have terminated. * Fix make distclean and make distcheck. * Also skip route not configured with down interface. Otherwise, if keepalived has virtual_routes configured, we create a virtual interface and bring it up and down, current code will bring VRRP state to FAULT and never return. * Stop vrrp process entering infinite loop when track script times out Issue #1093 identified that the vrrp process was entering an infinite loop after a track script timed out. This was due to a child process thread having an RB tree for PIDs as well as for the timeout, and if a child process timed out, the thread wasn't being removed from the PID RB tree. This commit now ensures it is removed. * Fix the abbreviation of Shortest Expected Delay. * Don't free unallocated memory if not tracking processes. * vrrp: Rewrote JSON code Remove dependency to json-c extralib by using a simple streaming JSON writter. Refactored code to make it simple to maintain. * vrrp: Fix JSON handling for v{route;rule}. * autoconf: fix nftables selection We need to inhibit nftable compilation if compiling system has kernel header file nf_tables.h but not libnftnl nor libmnl. 2018-11-12 Alexandre Cassen * keepalived-2.0.10 * Fix compiling on Alpine Linux. * Stop printf compiler warning on Alpine Linux due to rlim_t. * manpage cosmetic. * Fix removing snmpd read threads when snmpd becomes unavailable. * Update to support libipset version 7. * Use ipset_printf for ipset messages so can go to log. * When opening files for write, ensure files can only be read by root. Issue #1048 referred to CVE-2018-19046 regarding files used for debugging purposes could potentially be read by non root users. This commit ensures that such log files cannot be opened by non root users. * Disable fopen_safe() append mode by default If a non privileged user creates /tmp/keepalived.log and has it open for read (e.g. tail -f), then even though keepalived will change the owner to root and remove all read/write permissions from non owners, the application which already has the file open will be able to read the added log entries. Accordingly, opening a file in append mode is disabled by default, and only enabled if --enable-smtp-alert-debug or --enable-log-file (which are debugging options and unset by default) are enabled. This should further alleviate security concerns related to CVE-2018-19046. * vrrp: add support to constant time memcmp. Just an update to use best practise security design pattern. While comparing password or hmac you need to ensure comparison function is time constant in order to figth against any timing attacks. We turn off potential compiler optimizations for this particular function to avoid any short circuit. * Make sure a non privileged user cannot read keepalived file output Ensure that when a file such as /tmp/keepalived.data is wriiten, no non privileged can have a previous version of that file already open, thereby allowing them to read the data. This should fully resolve CVE-2018-19046. 2018-11-08 Alexandre Cassen * keepalived-2.0.9 * Fix updating a timer thread's timeout. Issue #1042 identified that the BFD process could segfault. This was tracked down to a timer thread which had already expired having its timeout updated by timer_thread_update_timeout(). The sands timer should only be updated if the thread is on a waiting queue, and not if it has already timed out or it is unused. * Don't requeue read thread if it is not waiting. This update matches commit 09a2a37 - Fix updating a timer thread's timeout should. * Allow BFD instance to recover after send error. If sendto failed in bfd_send_packet(), the bfd instance was put into admin down state, but there was no means for the bfd instance to transition out of admin down state. This commit makes keepalived log the first instance of a sequence of failures to send a bfd packet, but does not bring the bfd instance down in case the error is a transient error. If the error is longer lasting, the remote system will timeout, transition to down state, and send a message saying it is down. Once the bfd instance can start sending again the bfd instance can now transition again to up state. * Make DGB definition use log_message() rather than syslog(). * Fix building with --enable-debug configure option. * Start list of required kernel features in INSTALL file. Issue #1024 asked what kernel features are needed to support keepalived. The simple answer was that it isn't recorded anywhere, so this is a start of making a list of the features required. * Make list_remove() call list free function and add list_transfer(). If an element is being removed from a list, the free function should be called. list_transfer() allows a list element to be moved from one list to another without freeing and reallocating the list element control information. * Add mem_check diagnostics re calling functions of list functions. When using mem_check, mallocs and frees were recorded against the list functions, and the originating functions weren't identified. This patch adds recording of the functions calling the list functions so that the originating function is identified. * Simplify the processing of comments in configuration files. This commit moves the handling (and removal) of comments to a single function (called from read_line()) which simplifies the processing of config files. * Add ~SEQ(start, step, end) config functionality Where a configuration has repeated blocks of configuration where the only thing that changes is a numeric value (e.g. for VRIDs from 1 to 255) this allows the block to be defined once, and a single line using ~SEQ can then generate all the blocks. * Use REALLOC when building a multiline definition. The code used to use MALLOC, strcpy() and FREE, but REALLOC can do all this for us. * Improve mem-check diagnostics. When using an allocation list of over 50,000 entries, it was quite slow searching thtough all the entries to find the matching memory allocation, and to find free entries. This commit changes to using malloc() to create entries, and a red-black tree to hold the entries. It also has a separate list of free entries. This commit also adds 4 more types of memory allocation error, and improves the consistency of the entries in the log files. * Don't attempt to delete VMAC when underlying interface is deleted. If the underlying interface of one of our vmacs is deleted, and we know the vmac has been deleted, don't attempt to delete it again. * Include master state in determining if vmacs are up or down Netlink doesn't send messages for a state change of a macvlan when the master device changes state, so we have to track that for ourselves. * Turn off parser debugging. * Make test/mk_if create iptables chains. * Handle interfaces not existing when keepalived terminates. If the underlying interface of a vmac we created has been deleted, the vmac will not exist so don't attempt to delete it again. Also, don't attempt to reset the configuration of the underlying interface. * Handle the underlying interface of a macvlan interface going up/down. The kernel doesn't send netlink messages for macvlans going up or down when the underlying interface transitions (it doesn't even update their status to say they are up/down), but the interfaces don't work. We need to track the state of the underlying interfaces and propagate that to the macvlan interfaces. * Fix duplicate value in track_t enum. * Fix check for matching track types. * Treat macvtap interfaces in the same way as macvlan interfaces. * Improve handling of interfaces not existing when keepalived starts. * Fix handling interface deletion and creation of vmacs on macvlan i/fs. * When interface created, open sockets on it if used by VRRP directly If an interface is created that has vrrp instances configured on it that don't use VMACs, or use vmac_xmit_base, then the raw sockets must be opened. * Force seeing a transition to up state when an interface is created. * Fix netlink remnant data error. * Add command line and configuration option to set umask. Issue #1048 identified that files created by keepalived are created with mode 0666. This commit changes the default to 0644, and also allows the umask to be specified in the configuration or as a command line option. * Fix compile warning introduced in commit c6247a9. Commit c6247a9 - "Add command line and configuration option to set umask" introduced a compile warning, although the code would have worked OK. * When opening files for write, ensure they aren't symbolic links. Issue #1048 identified that if, for example, a non privileged user created a symbolic link from /etc/keepalived.data to /etc/passwd, writing to /etc/keepalived.data (which could be invoked via DBus) would cause /etc/passwd to be overwritten. This commit stops keepalived writing to pathnames where the ultimate component is a symbolic link, by setting O_NOFOLLOW whenever opening a file for writing. This might break some setups, where, for example, /etc/keepalived.data was a symbolic link to /home/fred/keepalived.data. If this was the case, instead create a symbolic link from /home/fred/keepalived.data to /tmp/keepalived.data, so that the file is still accessible via /home/fred/keepalived.data. There doesn't appear to be a way around this backward incompatibility, since even checking if the pathname is a symbolic link prior to opening for writing would create a race condition. * Make netlink error messages more meaningful. * Fix compiling without support for macvlans. * fix uninitialized structure. The linkinfo and linkattr structures were not initialized, so we should not expect that unexistant attributes are set to NULL. Add the missing memset(). * fix socket allocation with dynamic interfaces. When there are several vrrp instance binding different interfaces that don't exist at startup, their ifindex is set to 0 in the sock. The function already_exist_sock() that lookup for an existing socket will always return the first sock because the ifindex is the same. Later, when an interface appears, the fd will be created for one instance, and all instances will wrongly use this fd to send the advertisments. Fix this by using the interface structure pointer instead of the ifindex as the key for sock lookup. The problem was identified by Olivier Matz who also provided a patch fixing the problem. This patch is a slight rework of Olivier's patch, better using the existing data structures that keepalived already holds. * When creating a macvlan interface, use AF_UNSPEC rather than AF_INET. * Stop using libnl for configuring interfaces. Since there is code to configure the interfaces using netlink without using libnl, there is no point in having code to do it using libnl. * Fix building on Centos 6.5. * Stop including some files not needed after libnl removal for i/fs. * Fix some compilation issues when building without vrrp support. * Stop using linbl for mcast group membership and setting rx buf sizes. Since there is code to handle multicast group membership and setting kernel netlink receive buffer sizes without using libnl, there is no point in having code to do it using libnl. This now means that the vrrp functionality no longer uses libnl. * Add some sanity checking of configure options. Certain invalid combinations of configure options could cause compile errors, e.g. --disable-vrrp --enable-vrrp-fd-debug. This commit ensures that invalid combinations aren't allowed, in order to stop the compile errors. * Fix invalid configuration combination caught by previous commit. * Use netlink to set/clear rp_filter on interfaces. * Fix configure for building without vrrp. * Actually update the .travis.yml file to fix the problem. * Fix conditional compilation re epoll-thread-dump debugging. * Update INSTALL file now no longer use libnl-route-3. * Stop cast to incompatible function type warnings from gcc 8.1. * Update snapcraft.yaml not to include libnl-route-3. * keepalived exit with non-zero exit code if config file not readable. * Allow specifying default config file at configure time. * Use keepalived define for exit code when malloc failure. * Fix configuring fixed interface type. * Add configuring keepalived default configuration file. * Fix return value in get_time_rtt() error path. * Update generation of git-commit.h. * snapcraft.yaml: Enable all sensible build options. Preserve build time version in the snap version. Expose genhash. * snapcraft.yaml: Build keepalived with Linux 3.13 headers. * snap: Add an install hook to make sure a keepalived configuration exists. * snap: Move the hooks to the correct location. * snap: Make sure /etc/keepalived exists. * Fix building with IP_MULTICAST_ALL in linux/in.h but not netinet/in.h Issue #1054 identified that configure was checking the definition of IP_MULTICAST_ALL in linux/in.h but including netinet/in.h, which also has the definition, but only from glibc 2.17. This commit creates a local definition (in lib/config.h) of IP_MULTICAST_ALL if it is defined in linux/in.h but not in netinet/in.h. The reason for this is that compiles using linux/in.h fail due to conflicting definitions. * Fix creating iptables tables in mk_if. * Update .travis.yml to use xenial. * Update .travis.yml to add --enable-regex option. * Tidy up .travis.yml file. * snap: Build multiple keepalived binaries. * Updated snapcraft builds to support multiple kernel versions. 2018-10-21 Alexandre Cassen * keepalived-2.0.8 * Improve identifing interface as macvlan when reading interface details * Enslave a VMAC to the VRF master of the underlying interface. * Use addattr32 rather than addattr_l for if_index. * Only include VRF support if kernel headers support it. * Fix --enable-timer-debug configure option. * Fix some configure.ac enable option tests. * Include stdbool.h in process.c. * Fix diagnostic message re ignoring weight of tracked interface. * Fix track_bfds with weights. * Correct conditional compilation definition name. * Fix memory leak in HTTP_GET/SSL_GET. * Fix two memory leaks in DNS_CHECK. * Don't consider retries for BFD_CHECK. The BFD_CHECKer doesn't support retries, and the check was causing the checker not to transition to down state. * Fix memory leak with BFD_CHECK. * Restart global notify FIFO handler after reload. * modify @WITH_REGEX@ to @WITH_REGEX_TRUE@ * Fix compiling without BFD support. * Stop bfd process sending double the number of packets. If a bfd process received an initial bfd packet, it scheduled a second bfd_sender_thread thereby causing two packets to be sent in every interval. * Use timerfd for select timeouts rather than select timeout parameter This is a precursor to moving to using epoll. * Use epoll rather than select. epoll is both more efficient than select and also doesn't have a file descriptor limit of 1024, which limited the number of vrrp instances that could be managed. This commit also introduces read-black trees and the list_head list type. * Add --enable-timer-check option for logging calls for getting time Calls to update the current time from the kernel are made too frequently, and this patch logs when the calls are made, and how long since the previous call, so unnecessary calls can be removed. * Add debug option for monitoring epoll queues. This is enabled by --enable-epoll-debug and replaces --enable-timer-debug. * Use system monotonic clock to generate a monotonic clock. Rather than have our own code for creating a monotonic clock, use the kernel's monotonic clock. * Make some functions in timer.c inline. The functions had one line of code so inlining them is more efficient. * Fix requeueing read and write threads after read/write timeouts. * Fix initial allocating and final freeing of thread_master epoll_events. * When cleaning up threads, also clean up their thread_events. * Add thread_close_fd() function to release thread_event_t on close When a file descriptor that has been monitored by epoll is closed the thread_event_t structure used for managing epoll for that fd has to be release. Therefore calls to close() and replace by calls to thread_close_fd(). * Make parent process write log entry when it is reloading. * Move checking for thread timeouts to timerfd_handler There is no point in checking for thread timeouts if the timerfd isn't readable; in other words only check for thread timeouts if the timer has expired. * Make bfd reschuling timer threads more efficient. * Streamline DNS_CHECK code. * Fix buffer overrun with track file path names. * Add timestamp when writing mem_check entries to file. * Ensure thread_event_t released for ready threads at termination. * Increase open file limit if large number of VRRP instances. Each VRRP instance can use up to 2 file descriptors, and so if there are more than 500 ish VRRP instances the number of open files can exceed the default per process limit (1024 on my system). The commit allows 2 file descriptors per vrrp instance plus a few more, and if the RLIMIT_NOFILE value returned by getrlimit isn't high enough, keepalived will increase the limit. * Ensure that child processes run with standard priorities/limits. When child processes such as notify scripts, track_scripts and MISC_CHECK scripts are run, they should not inherit any elevated priorities, system limits etc from the parent keepalived process. * Change multiple spaces to tabs in scheduler.h. * Add family to sockpool listing. * Fix a multiline definition expansion issue. * Free allocated cache when closing/freeing netlink socket. When running on a system with 500+ interfaces configured and adding 1000 VMAC interfaces, the heap was growing by 340Mb due the netlink cahce not being freed after creating each VMAC interface. With this patch the heap only grow by 3.7Mb (if creating 1000 VMAC interfaces the heap grep by 905Mb now reduced to 6.1Mb). * Stop using netlink cache when adding and configuring VMAC interfaces. When running on a system with 500+ interfaces configured and adding 1000 VMAC interfaces, it was taking 2.3 seconds to add the interfaces. Without populating a netlink cache each time a VMAC interface is created it now takes 0.38 seconds to add the interfaces (if creating 1000 VMAC interfaces it was taking 6.1 seconds, now reduced to 0.89 seconds, and the heap growth is reduced from 6.1Mb to 3.9Mb). * Add function rtnk_link_get_kernel for dynamic linking. * Fix compiling without JSON support. * Add support for recording perf profiling data for vrrp process. * Add comment re usage of MAX_ALLOC_LIST. * Some streamlining of scheduler.c. * Merge --enable-epoll-debug and --enable-dump-threads functionality. * Let thread_add_unuse() set thread type, and use thread_add_unuse() more. * Use break rather than return in process_threads(). * Fix segfault when reloading with HTTP_GET and no regex configured. * Merge the next-generation scheduler. * Make all debug options need enabling at runtime. Previously if configure enabled a debug option its output was always recorded, which meant that if one didn't want the output, configure/ compile was needed. This commit adds command line options that need to be set in order to turn the debugging on. * Remove unwanted debug message. * Fix parsing --debug options. * Fix rb tree insertion with timers. * Add missing functions for thread debugging. * Add vrrp instance VMAC flags when dumping configuration. * Ensure parent thread terminates if child has permanant config error. * Ensure don't delete VMAC interface if keepalived didn't create it. and sundry fixes. * If receive lower priority advert, send GARP messages for sync group. A recent update to issue #542 identified that following recovery from a split brain situation, GARP messages weren't being sent. It transpired that, if a member of a sync group in master state received a lower priority advert and vrrp_higher_prio_send_advert is set, a further (lower priority) advert is sent, and the instance and all the members of the sync group transition to backup (the other members of the sync group don't send a further advert since they haven't received a higher priority advert). This meant that the other members of the sync group on the keepalived instance that remained master didn't receive a lower priority advert, and so didn't send further GARP messages. This commit changes keepalived's behaviour, so that if a vrrp instance is sending GARP messages due to receiving a lower priority advert and it is a member of a sync group, keepalived will also send GARP messages for any other member of the sync group that have garp_lower_prio_rep set. * Allow 0.0.0.0 and default/default6 for rule/route to/from addresses. * Check return value of SSL_CTX_new(). * Check return values of SSL_new() and BIO_new_socket(). * Only allow subnet masks with routes or virtual IP addresses. For example, if specifying a via address or preferred source address for a route, it isn't valid to specify a subnet mask. * Add inet/inet6 to specify ip route/rule family if ambiguous. * Remove superfluous parameter from parse_route(). * Add "any" and "all" as synonyms for "default". * Fix memory leak if route destination address is wrong address family. * Add ttl-propagate route option. * Fix checking return status of kill(). * Fix building with --enable-debug configure option. * Stop delay in reload when using network namespaces. If running in a network namespace, getaddrinfo() could take over 30 seconds before timing out while trying to contact a name server. To alleviate this, the hostname is remembered from when keepalived started. * Fix spelling of propagate in propagate_signal(). * Fix effective_priority after reload if tracked interface down. * Cosmetic grammatical changes. * Add debug option for dumping vrrp fd lists. * Fix calculation for vrrp fd timers. Starting or reloading keepalived when an interface that was tracked interface was failed was stopping other vrrp instances that were on the same interface but not using VMACs coming up. * Move code for initialising tracking priorities to vrrp_track.c. * Don't overwrite track file on reload. * Don't attempt to write track file if path not specified. * Fix compiling when not using --enable-vrrp-fd-debug. * Fix compiling with configure --enable-vrrp-fd-debug. * Add sync group track_bfds and track file status to config dump. * Move initialisation of track_files. * Don't alter effective_priority if track_file take vrrp instance down. * Don't log vrrp instance in fault state at reload if already fault. * Fix calculating fd timer if all vrrp sands are set to TIMER_DISABLED. * Don't make all sync groups transition to backup on reload If a sync group was in master state, and can still be after a reload then allow it to stay in master state. * Don't have track_bfd list in vrrp_sgroup_t in BFD not enabled. * Fix memory leak re vrrp_sgroup_t track lists. * Tidy up some freeing of MALLOC'd memory. Use FREE_PTR if it is not known if the pointer is valid, and don't clear the pointer afterr FREE/FREE_PTR since FREE does it anyway. * Add memory.c list size definition and move definition from memory.h. * Increase size of checksum value for MEM_CHECK. * Don't store checksum of memory allocation block. It can be calculated from the size, so do so. * Make the checksum for memory allocation blocks unsigned. * Use an enum for memory allocation block types. * Update comment re debug bit for memory detect error. * In memory alloc debug code report free or realloc for not alloc'd. * Allow for PIDs up to 2^22 (7 decimal digits). * Add function for dumping memory allocation while running. * Fix max memory allocation size calculations. * Fix reporting original and new file/line/func for realloc. * Check matching block for realloc is allocated. The same memory block may have been previously allocated and freed, so we need to make sure that the block we find is currently marked as allocated. * Use a new MEMCHECK struct for realloc overrun detected It was marking the allocated block as an overrun block, whereas it needs to be an allocated block, so use a new block to mark the overrun. * Tidy up working of a couple of memory allocation messages. * Use for loops rather than while blocks in memory allocation code. * Report number of mallocs and reallocs with MEMCHECK. * Attempt to log first free after double free in MEMCHECK. * Streamline use of buf/buffer in memory.c. * Always use first free entry in alloc_list for MEMCHECK. * Define MEMCHECK alloc_list size via configure. * Align keepalived_free() and keepalived_realloc(). * Make char * const where possible for MEMCHECK. * Merge MEMCHECK keepalived_free() and keepalived_realloc(). Most of the code was common between the two (or should have been), so it makes sense for them to use common code. * Ensure only relevant thread types run during shutdown. * Fix building without --enable-mem-check. * Use rbtree search for finding child thread on child termination. It was doing a linear search of the rbtree in timeout order. This commit adds another rbtree for child processes (vrrp track scripts and check_misc scripts), sorted by PID, to make the search by PID more efficient. * Make rbtree compare function thread_timer_cmp() more efficient. * Remove child_remover functionality - it was superfluous. * Fix checking that there are no duplicate vrrp instances configured The tuple {interface, family, vrid} must be unique. The check for this was being made completely incorrectly. * Delay creating vrrp notify FIFO. * Remove struct sockaddr_storage saddr from sock_t. * Use an rbtree for finding vrrp instance for received advert. Previously the code search a list of pointers to vrrp instances and looked for a matching fd and vrid. In order to optimise this, it was implemented using an mlist whose index was a hash of the fd and vrid. This commit changes the approach and uses an rbtree for each sock_t. Since the sock_t that the advert was received on is known, the rbtree search is only searching for a match on the vrid. Not only is this more efficient, but it is simpler, uses standard code, and reduces the code by over 60 lines. * Use an rbtree for finding vrrp instance for socket timeout. Previously the code search a list of pointers to vrrp instances and looked for matching file descriptor and sands < time_now. In order to optimise this, it was implemented using an mlist whose index was a hash of the fd. This commit changes the approach and uses a second rbtree for each sock_t. Since the sock_t that the timeout occurred on is known, the rbtree search is only searching for a match of the sands. Not only is this more efficient, but it is simpler, uses standard code, and reduces the code by over 220 lines. * Remove superfluous checks of rbtree node != NULL in rb_move(). * Remove superfluous check of node != NULL in rb_next(). * Update rbtree code to Linux 4.18.10. * Fix debug logging of sands timers before time_now. * Update rb_for_each_entry etc and rb_move to use rb_entry_safe. With the added definition of rb_entry_safe in the rbtree code updated to Linux 4.18.10, the refinition of rb_entry was reverted to the kernel definition. That meant that rb_for_each_entry, rb_for_eacn_entry_safe and rb_move neded to be updated to use rb_entry_safe rather than rb_entry. * Add support functions for rbtree rb_root_cached. This is in preparation for the use of rb_root_cached in the next patch. * Use cached rbtrees where the key is a timeval_t sands When the key of an rbtree is a timeval_t sands keepalived will frequently need to access the first node of the tree in order to calculate the next timeout. This applies to the read, write, child and timer threads queues, and also the vrrp queues on a sock_t. The use of cached rbtrees for these is ideal since it gives direct access to the first node of the queue. * Add thread_add_read_sands to avoid introducing timer errors. When using thread_add_read and the timeout was held as timeval_t, it was converted to and offset from time_now, and then converted back to a timeval_t, but time_now was updated, resulting in a slightly different value being used as the timeout. Using thread_add_read_sands() avoids the double conversion and results in the timeout being more accurate. * Replace NETLINK_TIMER with TIMER_NEVER. It makes the code easier to read, and since NETLINK_TIMER was defined to be TIMER_NEVER it doesn't change the functionality. * Handle preempt delays not expiring at same time on sync group If different vrrp instances in a sync group had preempt delays that expired at different times keepalived looped with very small to epoll_wait() until all preempt delays had expired, causing high CPU utilisation. Keepalived now reschedules vrrp instances with a delay of 3 * advert_int + skew time while waiting for all vrrp instances in the sync group to expire their preempt delays. * Fix segfault when receive netlink message for default route added. * Move vrf_master_index into conditional compilation block. * Store interface macvlan type. * Make vrp_master_ifp point to self for VRF master interfaces. * Log if cannot create a VMAC due to existing interface with same name. * Handle delete/create of macvlan i/fs which aren't keepalived's. * Tidying up keepalived_netlink.c. * Handle VRFs changing on macvlan i/fs which have VMACs configured on them. * Fix recreating our VMACs if they are deleted. * Fix detecting address add/deletion from underlying i/f of our vmacs. * Don't use configured_ifp or base_ifp if not _HAVE_VRRP_VMAC_. * Distinguish between VMAC on real i/f and no VMAC on macvlan i/f If keepalived is configured to have a non VMAC interface on a macvlan interface, we want to use the macvlan interface rather than the underlying interface, whereas if we have a VMAC interface on a macvlan interface, we create the VMAC on the underlying interface of the macvlan. * Update duplicate VRID check where vrrp instance configured on macvlan. If a VRRP instance is configured on a macvlan interface, the duplicate VRID check needs to be done on the underlying interface. * Check for VRID conflicts when changeable interfaces are added For example, a vrrp instance could be configured on a macvlan, and that macvlan could be deleted and recreated with another base interface. The VRIDs in this case need to be checked for duplicates against the base interface, and so the VRID check needs to be done dynamically. In order to allow VRID conflicts to produce config errors at startup, by default keepalived assumes that there won't be interface movements as described above, and will only handle it if the global_defs option 'dynamic_interfaces' is used along with the option 'allow_if_changes'. * Remove some comments inserted for tracking changes to code. * Fix building with --enable-debug configure option. * Check that '{'s and '}'s are balanced in the configuration file. * Allow more flexibility re placing of { and }. * Improve reporting additional '}'s in configuration. * Minor improvements re thread handling and cancellation. * Remove unused THREAD_IF_UP and THREAD_IF_DOWN. * Replace getpagesize() with sysconf(_SC_PAGESIZE). * Increase netlink receive buffer for dumps to 16KiB. * Dynamically set the netlink receive buffer size. * Sort out setting netlink receive buffer size. 2018-08-23 Alexandre Cassen * keepalived-2.0.7 * Fix buffer overflow in extract_status_code(). Issue #960 identified that the buffer allocated for copying the HTTP status code could overflow if the http response was corrupted. This commit changes the way the status code is read, avoids copying data, and also ensures that the status code is three digits long, is non-negative and occurs on the first line of the response. * Some fixes for config-test. * Change ka_config_error() to report_config_error(). * Read interface addresses when doing config-test. * Update documentation re garp_lower_prio_repeat. * Add comment re tracking routes with nexthops doesn't work. * Fix handling of default_interface Issue #963 identified that default_interface wasn't being set correctly. The problem was that the configuration was read by the parent process, but the parent process doesn't know about the system interfaces. Fix commit makes the vrrp process set the default interface when it starts. * Fix a segfault in checker process on reload Issue #955 identified a segfault when keepalived reloads. This was caused by attempting to set the receive buffer size on a netlink socket that was not open. It now only attempts to set buffer sizes on the netlink sockets that are open. * Use report_config_error() in check_parser.c. * Don't run a sublevel close handler on a skipped configuration block If a configuration block was skipped due to an error, the configuration read won't be valid and may not even exist, so make sure the sublevel end handler isn't run. An example is if a virtual_server block is skipped, then the sublevel end handler would have run against the previous (if any) virtual_server, and if there hadn't been a previous virtual_server block it could segfault. * Tidy up use of inet_stosockaddr. * Add more error checking to read_timer() and its uses. * Add validation of lvs_sched. * Use report_config_error() in checker parsers Thwese should have been included in commit ead70947 - "Update config-test". * Add stronger validation of numeric fields Issue #955 identified that invalid parameters in advert_int, delay_loop, lb_algo/lvs_sched, lb_kind/lvs_method, quorum, ip_family, virtual_server and real_server ports, weights and connect_timeout were not being reported. This commit will now report any errors in those fields, and a number of other fields. * Improve parsing of virtual_router_id. * Allow virtual server ports not to be specified If the service is persistent, a "wild-card" port can be used. * Prepend WARNING to read_int/unsigned/double messages if not rejecting read_int()/read_unsigned()/read_double() can output log messages if the syntax is invalid but the configuration is being accepted, e.g. parsing 12zyx will return 12 if _STRICT_CONFIG_ is not defined. In this case we want to indicate that the entry is not valid, but we are still processing it, so prepend the error message with WARNING. * Improve parsing of virtual server group address ranges An address range of 10.9.8.7-10.9.8.15 was parsed as 10.9.8.7-10 and no error was reported, although someone might have expected that this would mean 10.9.8.7-15. keepalived will now report a configuration warning, and if keepalived is configured with --enable-strict-config-checks the configuration will be rejected. * Restore original string in inet_stosockaddr() If there was a '-' or a '/' after the address, the string was modified to terminate at that point. This commit now restores the original string. * Allow keepalived to run with --config-test when live instance running. * Report errors for invalid smtp_server. * Remove inet_stom() - it was not used inet_stom used atoi which is unsafe. Since the function was not used, it has been simply removed. * Rename read_(int|unsigned|double) read_(int|unsigned|double)_strvec Want to be able to have equivalent functions just being passed a string, so rename the functions using strvec to be explicit about that. * Fix config dump for vrrp_garp_lower_prio_delay. * Fix config dump of vrrp garp_refresh. * Make config dump write fraction part of vrrp preempt delay. * Simplify config dump of garp/gna_interval. * dd config dump for VRRP/checker/BFD realtime_priority/limit. * Ensure structure fully initialised for sched_setscheduler. * Make set_process_dont_swap() and set_process_priority() static functions. * Minimise time when keepalived runs with realtime priority keepalived shouldn't use realtime priority when it is loading or reloading configurations, so delay setting realtime priorities, and revert to stardard scheduling when terminating or reloading. * Report user/system CPU time used when exit with detailed logging. * Make default rlimit_rtime 10000 microseconds The previous default of 1000 microseconds was insufficient * Stop using atoi for readong configuration weight. * Make read_unsigned, read_int, read_double for parsing strings These functions are analogous to read_unsigned_strvec, read_int_strvec and read_double_strvec, but take strings as the parameter rather than a strvec. * Stop using atoi for parsing mask of ip addresses. * Stop using atoi for reading VRID in Dbus code. * Stop using atoi for reading vrrp debug level, and add to conf dump. * Stop using atoi for parsing garp_refresh. * Stop using atoi for parsing preempt_delay. * Stop using atoi for parsing garp_lower_prio_repeat. * Stop using atoi for parsing vrrp script rise. * Stop using atoi for parsing vrrp script fall. * Stop using atoi for parsing garp_interval/gna_interval * Stop using atoi for parsing smtp status code * Stop using atoi for parsing realtime scheduling priority * Stop using atoi for parsing process priorities * Stop using atoi for parsing global garp/gna_interval * top using atoi for parsing genhash port number * Stop using atoi for parsing tcp_server port number * Stop using atoi for parsing command line log facility. * update documentation to show range of bfd weights * Ensure read_unsigned() detects negative numbers. * Stop using atoi to parse HTTP_GET status code. * Stop using atoi to parse checker port numbers. * Use read_unsigned() for domain/inet_stosockaddr port. * Make read_unsigned_strvec() recognise minus sign after spaces It is possible have a word read from the configuration start with spaces it is is enclosed in quotes, which are then removed, but the leading spaces aren't removed. * Make get_u8()/get_u16()/get_u32() use read_unsigned(). * Make get_u64() properly detect -ve numbers. * Make get_time_rtt() properly detect -ve numbers. * Make get_addr64() handle whitespace properly and disallow '-' signs Skip leading whitespace, don't allow embedded whitespace, and don't allow minus signs. * Make get_addr64() and parse_mpls_address handle whitespace * Change more log_message() to report_config_error() in vrrp_iproute.c. * Improve use of strtoul() in rttables.c. * Implement get_u64() in same way as get_u8() etc. * Fix print format specifier in read_unsigned_base * Correct error message for garp_master_refresh * Remove \n from error message. * Allow round trip time to be UINT32_MAX * Change mix_rx to min_rx for bfd_instance in documentation. * Make bfd_parser use read_unsigned_strvec() etc rather than strtoul(). * Fix config dump for BFD instance timers. * Fix handling of ip rule ipproto option. * Add read_unsigned_base_strvec() to allow number base to be specified This requires renaming static functions read_int_int() etc to read_int_base() etc. * Minimise and improve use of strtoul() etc in parsing ip rules. * Use read_int_strvec() for vrrp_version. * Use read_int_strvec() instead of strtol() in vrrp_parser.c. * Use read_int_strvec() instead of strtol() etc in check_parser.c. * Use read_int_strvec() instead of strtol() etc in check_data.c. * Improve strtoul handling for '--log_facility' command line option. * Add documentation for lvs_timeouts config option. * Allow vrrp garp_delay to be 0 in accordance with documentation. * Use read_int_strvec() instead of strtol() etc in global_parser.c. * Corret end of line detection in genhash. * Improve genhash command line parsing use of strtoul. * Replace CHECKER_VALUE_INT with read_unsigned_strvec due to use of strtoul. * Remove CHECKER_VALUE_UINT definition since no longer used. * Add conditional compilation around variable not always used. * Only read interfaces in VRRP process. * Enable --config-test to work with BFD configuration. * Only add garp_delay_t's to interfaces used by VRRP instances There is no point allocating a garp_delay_t to an interface that isn't used by a VRRP instance, so this commit make keepalived only allocate garp_delay_t scructures to the used interfaces. In addition, when the configuration is dumped, list the interfaces relevant to each garp_delay_t. * Add logging command line options if keepalived segfaults. * Don't free tcp checker data field on exit, since not used. * Report configuration file line numbers when errors Following the recent series of commits for better validation of the configuration, and the move to reporting all configuration errors through report_config_error(), it is now feasible to report the configuration file line number on when the error occurs. * Rationalise error messages when reading configuration Now that configuration file line numbers are reported, the error messages can be simplified since the context doesn't need to be given in the detail as before. * Return NULL rather than false as a pointer in parser.c * Fix an infinite loop when parsing certain parameter substitutions If a multiline definition had text after the '=' sign, keepalived would loop. * Fix a multiline parameter substitution having a replacement on 1st line If a multiline parameter definition had a replaceable parameter or a definition on the first non-blank line, it wasn't being handled. This commit ensures that replaceable parameters/definitions on the first line are handled correctly. * Add logging command line options when keepalived starts. * Change some log_message() to report_config_error() in vrrp_sync.c. * Improve handling of select() returning an error If select returned an error, the code was processing the returned timeout and fds as though they were valid. This commits logs an error the first time, sleeps for 1 second if it is a programming error, and then sets up the select call again. * Remove DBG() statement left over in previous commit. * improve doc spelling. * Add mh scheduler for LVS This is similar to the sh scheduler. Options are the same but we duplicate everything. An alternative would have been to reuse the names for the sh scheduler. The mh scheduler is supported starting from Linux 4.18. It's a consistent hash scheduler. * manpage update and re-visited. keepalived.conf.5 be considered as THE exhaustive source of information in order to configure Keepalived. This documenation is supported and maintained by Keepalived Core-Team. * Fix errors in KEEPALIVED-MIB.txt Commit 181858d - "Add mh scheduler for LVS" introduced a couple of formatting errors, and didn't update the revision date. * Some SNMP library handling improvements. * Stop bfd -- vrrp pipe read timing out There is no need for a timeout on reading the pipe, so set the timeout to TIMER_NEVER. * manpage updates Update manpage to make html convertion easy. This manpage is now sitting in documentation tab of Keepalived website. * Update libnl_link.c SegFault when launch with dynamic LIBNL because of loading symbol from wrong library. * Fix building rpm package and instructions Issue #977 identified that the instructions in the INSTALL file were incorrect. * Adds regex pattern matching for HTTP_GET and SSL_GET. * Remove pcre build tests from Travis-CI Travis-CI environments are too old to support libpcre2, so we have to remove it. * Fix #980: coredump on start when logfile cannot be accessed. * Don't loop forever if configuration has unknown replaceable parameter. 2018-07-23 Quentin Armitage * keepalived-2.0.6 * Fix genhash digest calculation. The bracketting in HASH_UPDATE was wrong. * Bring keepalived(8) man page up to date. * Fix segfault when IPVS_DEST_ATTR_ADDR_FAMILY not defined. Issue #938 identified a segfault on the checker process when using CentOS/RHEL 6. It turned out that conditional compilation check for IPVS_DEST_ATTR_ADDR_FAMILY was not being handled correctly. * Don't create a link-local address for vmac when vmac_xmit_base is set Since commit 18ec95add483 ("Make vmac_xmit_base work for IPv6 instances") VRRP advertisements are sent from the base interface and not from the vmac interface when vmac_xmit_base is set. Therefore, there is no need to configure a link-local address on the vmac interface. This also means that we don't need to regenerate a link-local address for the vmac if the link-local address was removed from the base interface, or inherit a link-local address in case one was configured on the base interface. * Fix setting i/f params on a bridge underlying i/f of a VMAC Issue #944 identified that when the underlying interface of a VMAC interface was a bridge, keepalived was failing to set arp_ignore and arp_filter in the underlying bridge interface. The problem appears to lie in the libnl3 library. The description of the problem given in the issue report was: Problem is that ifi_family is set to AF_BRIDGE, whereas it should be set to AF_UNSPEC. The kernel function that handles RTM_SETLINK messages for AF_BRIDGE doesn't know how to process the IFLA_AF_SPEC attribute. This commit stops using libnl3 for setting/clearing arp_ignore and arp_filter, and directly constructs the netlink messages in keepalived. * Use RTM_NEWLINK rather than RTM_SETLINK for setting i/f options libnl3 uses RTM_NEWLINK rather than RTM_SETLINK for setting interface options when ifi_family is AF_UNSPEC, so update commit 9b2b2c9 - "Fix setting i/f params on a bridge underlying i/f of a VMAC" to do likewise. * Fix creating VMACs on 2.6.32 and earlier kernels RTM_NEWLINK didn't support specifying interface by name until Linux 2.6.33, and if using an earlier kernel, the netlink call failed. This meant that the VMAC was not enabled. * Fix setting arp_ignore and arp_filter on bridge interfaces. * Add diagnostic message if vrrp script time out and kill fails. * Fix compile errors and warnings when building with --enable-debug. * Don't do md5 check unless configured. * In http_handle_response() combine fetched_url and url fetched_url and url always pointed to the same url, so only use one variable. * Store and handle HTTP_GET digest in binary form Configured digests were being stored in character string form, and the calculated digests were converted to strings. This commit now handles digests as fixed length binary data, and validates the configured digests to make sure they are valid hex strings with the correct length. * Add support for quote and escape handling of notify and other scripts. Notify and other scripts need to be able to be configured with embedded spaces, quotes and special characters for the command and the parameters. This commit adds that ability. * When checking script file path, only replace name part if same file. Some executables are in the filesystem as symbolic links, and alter their functionality based on the file part of the name. This was being incorrectly handled by keepalived, which now checks whether a file exists using the original name, and it it does whether it is the same file. * Remove cmd_str from notify_script_t The cmd_str string (sort of) duplicated what was in the args array of a notify_script_t, but was not always accurate. With the removal of cmd_str, whenever it needs to be output, the string is now generated from the args array, so accurately reflects what is actually executed. * Add quoting and escaping for script configuration, and other minor changes. * Use vsyslog() if available instead of syslog(). * Report virtual server as well as real server when config dump checker. * Only report IP_MULTICAST_ALL unset for IPv4 sockets Commit 6fb5980 - "Stop receive message queues not being read on send sockets" added a warning if data was received on vrrp send sockets, since setting IP_MULTICAST_ALL should stop packets being received, but older kernels still queued packets. It has now been discovered the IP_MULTICAST_ALL (of course) only applies to IPv4 and so the warning only makes sense for IPv4 sockets. I haven't been able to find a way to stop IPv6 multicast packets being received on the send socket. It appears that if any socket adds an IPv6 multicast group on an interface, then any raw socket using that interface will recieve all enabled multicast packets, and the receive socket has to add the multicast group. * Properly stop packets being queued on vrrp send sockets Commit 6fb5980 - "Stop receive message queues not being read on send sockets" did stop messages building up on the receive queue of vrrp send sockets, but it wasn't an ideal solution, and it also made the assumption that the problem was only occurring due to multicast packets not being filtered when IP_MULTICAST_ALL was set, which appears not to work properly between at least Linux 3.6.11 and 3.16. In fact the problem also occurred when using IPv4 unicast and IPv6 in any form, and so has been a long term issue in keepalived. The original solution was to listen on the send socket and discard any packets that were received. This commit takes a completely different solution (many thanks to Simon Kirby for the suggestion) and sets a BPF filter on send sockets that filter out all received packets on the sockets. This commit effectively reverts commit 6fb5980, and the subsequent commits 88c698d8 - "Cancel read thread on send sockets when closing", f981b55d - "Only allow vrrp_rx_bufs_policy NO_SEND_RX if have IP_MULTICAST_ALL", 7ff7ea1f - "Another fix to listening on send socket", and 77d947f7 - "Only report IP_MULTICAST_ALL unset for IPv4 sockets" and partially reverts 4297f0a - "Add options to set vrrp socket receive buffer sizes". This commit removes the configuration option NO_SEND_RX from vrrp_tx_bufs_policy introduced in commit 4297f0a since it is now no longer relevant, because no packets are queued to the send socket. * Add newlines to the keepalived.stats output for better readability. * Add notify_master_rx_lower_pri script option and FIFO output. If a lower priority router has transitioned to master, there has presumably been an intermittent communications break between the master and backup. It appears that servers in an Amazon AWS environment can experience this. The problem then occurs if a notify_master script is executed on the backup that has just transitioned to master and the script executes something like a `aws ec2 assign-private-ip-addresses` command, thereby removing the address from the 'proper' master. Executing notify_master_rx_lower_pri notification allows the 'proper' master to recover the secondary addresses. * Fix malloc'd memory length in open_log_file(). 2018-06-29 Quentin Armitage * keepalived-2.0.5 * Update config-test option so keepalived exits with status 1 on failure. * Fix config write of virtual server group ip addresses. * Document default and default6 for virtual/static route destinations. * Cancel read thread on send sockets when closing. Commit 4297f0a - "Add options to set vrrp socket receive buffer sizes" added reading on vrrp send sockets to stop receive queues building up on some 3.x kernels. The commit didn't cancel the read thread on the send sockets when the socket was closed, causing several thousand log writes. This commit cancels the read thread. * Exit with status 1 if config check fails, and fix terminating when reading send sockets. * Stop segfaulting when receive a packet (fixing commit 97aec76). Commit 97aec76 - "Update config-test option so keepalived exits with status 1 on failure" had a test for __test_bit(CONFIG_TEST_BIT) the wrong way round. This commit fixes that. * Don't assume rpm is available. * Only allow vrrp_rx_bufs_policy NO_SEND_RX if have IP_MULTICAST_ALL. * Improve setting up virtual/real servers with virtual server groups. When setting up virtual servers defined by virtual server groups, keepalived was getting confused between fwmarks and ip addresses. This still needs further work, but the setting up of virtual/real servers now works. * Fix setting up and deletion of virtual servers with groups Virtual server entries in virtual server groups can be used by multiple virtual_server entries. This commit ensures that virtual servers are not deleted until the last virtual_server instance using the virtual server is removed. * Allocate vrrp send buffer during vrrp_complete_instance() Issue #926 identified a segfault. The vrrp send buffer was not being allocated early enough, and was being accessed before being allocated if the checksum algorithm needed updating. * Fix vrrp v3 with unicast and IPv4. The checksum calculations were happening in the wrong way, with the wrong data. This commit sorts all that out. * Don't set effective priority to 254 when specify dont_track_primary. * Make csum_incremental_update16/32 inline. * Add --enable-optimise=LEVEL configure option. * Remove debug message left in configure.ac from adding --enable-optimise. * Fix compiling on CentOS 6. Issue #932 identified that keepalived would not compile on CentOS 6. The problem is that kernel header file linux/rtnetlink.h needs sys/socket.h to be included before linux/rtnetlink.h when using old (e.g. 2.6) kernel headers. * Another fix to listening on send socket. Commit 4297f0a - "Add options to set vrrp socket receive buffer sizes" added reading on vrrp send sockets to stop receive queues building up on some 3.x kernels. The commit didn't save the new thread in sock->thread_out when it was added for reading in vrrp_write_fd_read_thread. It now does so. 2018-06-17 Quentin Armitage * keepalived-2.0.4 * Make vmac_xmit_base work for IPv6 instances. Issue #917 identified that for IPv6 even when vmac_xmit_base was configured, the adverts were being sent from the vmac interface. This commit makes the packets be sent from the underlying interface when vmac_xmit_base is configured. * Handle vmac_xmit_base when interfaces are recreated. * Add -t config-test option. Issue #389 has received increasing support to add a configuration validation option. This commit adds the -t/--commit-test option to report any detected configururation errors and exit. Errors are logged to the system log by default, but use of the -g and -G options can make the errors be logged to files. 2018-06-15 Quentin Armitage * keepalived-2.0.3 * Fix building with --disables-routes configure option. * Fix some compiler warnings on Travis-CI. * Fix setting vrrp effective priority on reload. * Add tracking of static addresses, routes and rules By default a static address, route or rule will now be reinstated if it is deleted, unless the no_track option is specified. In addition, if a track_group is specified for an address/route/rule then if the address/route/rule cannot be reinstated (e.g. if the specified interface is down or has been deleted), then the vrrp instances specified in the track group will transition to fault state until the interface comes back up. This commit completes the monitoring and reinstatement of addresses routes and rules and means that keepalived should now fully support hot-swap devices. * Log when restoring static addresses, routes or rules. * Allow static addresses/routes/rules to be configured on VMACs. When VMACs were using an interface name generated by keepalived, if static address/routes/rules had beeon configured on a VMAC interface name, then a different name would be generated for the vrrp instance. This commit now allows the same name to be used. * Fix configure when pkg-config --libs returns -L entries. * Add log message for advert receive timeout when using log detail (-D). * Stop receive message queues not being read on send sockets. We shouldn't receive anything on vrrp send sockets since IP_MULTICAST_ALL is cleared, and no multicast groups are subscribed to on the socket. However, Debian Jessie with a 3.16.0 kernel, CentOS 7 with a 3.10.0 kernel, and Fedora 16 with a 3.6.11 kernel all exhibit the problem of multicast packets being queued on the send socket. Whether this was a kernel problem that has been subsequently resolved, or a system default configuration problem isn't yet known. The workaround to the problem is to read on the send sockets, and to discard any received data. If anyone can provide more information about this issue it would be very helpful. * Ensure sorry server/virtual server same address family unless tunnelled. A real server and a virtual server can only be of the same address family if the forwarding method is tunnelled, and also must have a kernel that supports IPVS_DEST_ATTR_ADDR_FAMILY. * Add options to set vrrp socket receive buffer sizes. Some systems have very large settings for net.core.rmem_default allowing very large receive queues to build if the sockets aren't read. keepalived doesn't need large buffers for receive queues, so this commit allows options for setting the maximum buffer sizes to be much smaller. It also adds the option of setting the receive buffer size on the vrrp send sockets to be as small as possible, since we shouldn't be receiving anything on those. Following commit 6fb5980 - "Stop receive message queues not being read on send sockets", this commit also adds the option not to read the send sockets, which can be used where it is known that the kernel will not queue unwanted multicasts to the send socket. * Consider eVIPs when determining if need GARP/NDISC send buffers. * Fix sending IPv6 unicast vrrp adverts. 2018-06-06 Quentin Armitage * keepalived-2.0.2 * Only compile code in rttables.c that is needed by the configuration. * Set default preferences for ip rules if not specified. Since different vrrp instances can become master in different orders, if preferences (priorities) are not specified for ip rules, the order in which they are specified will be indeterminate. In order to give some consistency, keepalived will not allocate a default preference to each rule, and will also warn that this is probably not going to work as intended. The solution is to specify a preference for each rule. * Require preference if an ip rule specifies goto. Since preferences are now auto-generated if not specified, a rule with a goto must now specify a preference. * Add tracking of virtual rules. If a virtual rule is deleted, the vrrp instance will transition to backup. When it becomes master again the rule will be re-added. * Fix compilation failure found by Travis-CI. * In configure.ac check if SHA1_Init() needs -fpic. * Fix make rpm when rpmbuild doesn't support --build-in-place. * Update INSTALL file to describe how to build rpm files. * Fix instructions for building rpm packages. 2018-06-04 Quentin Armitage * keepalived-2.0.1 * Remove '\n' characters from log_message() text. * Allow IPv6 ip rules to be specified using fwmarks. * Fix configure generation of keepalived.spec file. * Stop rebuilding scheduler.o every make. * Remove ' characters from configure args in keepalived -v output. * Remove duplicate reporting of network namespace in config dumps * Add ${_INSTANCE} config parameter. * Remove debugging log message. * Recalculate max_fd used for select if it should reduce. * Add tracking of virtual routes. If a virtual route is deleted, by default to vrrp instance will now transition to backup mode, and if it transitions to master again the route will be re-added. If an interface on which a route is configured is down, then the instance will go to fault state, since the route cannot be added. This commit also adds a no-track option for routes, which means that deletion of the route will not cause the vrrp instance to transition to backup. * Handle interface down at startup with tracked route configured on it If a virtual route which is tracked is configured on an interface that is down at startup, then the vrrp instance needs to start in fault state. * Rename netlink_reflect_filter() to netlink_link_filter() The function only handles RTM_NEWLINK/RTM_DELLINK messages and there are other functions to handle other message types. * Fix compilation warning. * Make recreating deleted VMACs work. * Fix Travis-CI compilation failure and warning. * Stop duplicate definition and duplicate include in vrrp_iproute.c. * Add new ip rule options for Linux 4.17 FRA_PROTOCOL, FRA_IP_PROTO, FRA_SPORT_RANGE and FRA_DPORT_RANGE have been added in Linux 4.17. 2018-05-26 Quentin Armitage * keepalived-2.0.0 * Beta branch merge into master branch ! 2years of dev here ! * Transition to master as soon as decision is made to do so Previously keepalived waited one further advert interval before transitioning. This meant that previously if a master went down and sent priority 0 message, there was one extra advert interval before the highest priority backup configured the VIP addresses. Now if vrrp instances have high priorities (i.e. close to 255), then the transition to master and configuration of addresses will now occur in a small multiple of advert_interval/256. * Process interface state changes immediately. Previously keepalived waited for advert timer expiry. The problem was that if an interface went down and came back up before the next timer expiry, and addresses, routes and VMACs that we had configured on that interface would be removed, but we wouldn't know about it. * Add support for hot-swappable NICs This also handles interfaces being deleted and restored. * Add vrrp_track_file option. This allows track_scripts, which are run on a frequent scheduled basis, to be replaced with a vrrp_track_file, which contains a number as a text string which is used in the same way as the exit status from a track script. The track_files are only read if they are changed, so external events can update a track file, rather than their status needing to be detected by polling by track scripts. * Add notify fifos. Rather than sending notifications via notify scripts it is now possible to send notify messages via fifos. Not only does this mean that the overhead of executing script for each notification is removed, but it also guarantees the delivery of notifications in the correct order, whereas if the notification is via scripts, there is no guarantee that the scripts will execute in the desired order if two or more notifications are sent in quick succession. There can be a global fifo to process all notifies, and also separate fifos for vrrp and checkers. It is possible to specify a script for keepalived to execute to process the messages on the fifo(s). * Stop logging address addition/deletions if addresses not ours The -a option can be used to override this behaviour and log all address changes. * Transition to fault state if source address for adverts is deleted from interface * Transition to backup state if a VIP or eVIP is removed When we next transition to master the addresses will be restored. If nopreempt is not set, that will be almost immediately. * Make address owner (priority 255) transition to master immediately * Don't process a received advert if the authentication fails * Ignore invalid received adverts totally Previously the master down timer was being updated, which meant that a backup could be stuck in backup state even if the only received adverts were invalid. * Don't reset timer before sending next advert if receive a lower priority advert. This was stopping a higher priority backup instance to stay in backup state. * Log if receive invalid authentication header * Ignore lower priority adverts when backup (to comply with RFCs) This also means that the master down timer wasn't reset, which was causing a delay to becoming master * Fix first advert interval of vrrp instances in a sync group. * Stop two vrrp instances with preempt delay and equal priorities flip-flopping between master and backup state * Make sync group members transition state at same time When first instance makes transition (i.e. when the trigger event occurs) rather than wait for next timer expiry * Process vrrp track script returning a new status code immediately For all instances (and their sync group members), rather than waiting for the next timer expiry on each instance, the instance will transition update it's state immediately. * On reload, make track scripts inherit the state from before reload This stops vrrp instances transitioning to down and coming back up once the script has run. * Correct the use of adver_int and master_adver_int * Ensure when leaving fault state that a vrrp instance transitions to backup unless it has priority 255 * Remove quick_sync functionality since no longer needed. * Improved code efficiency: * Finding vrrp instance after read timeout * When getting interface information for a new vmac, only request information for that i/f. * Directly update effective priority of vrrp instances when scripts return new status rather than scheduling a thread to do it * Don't run a read timeout on vrrp instance in fault state * Don't run a track script if no vrrp instance is tracking it * Stop checking interface status after every timer expiry since processing interface state changes is now done synchronously * The timeout for the select call had a maximum timeout of 1 second, it now times out only when something needs to happen * The timeout on netlink reads was 500 seconds and this has been extended to 1 day. * Streamline signal handling between main process and child process by using signalfd if available, rather than using a pipe * Minimise searching for an interface struct based on its index by using pointers to the interface structures * Stop opening and closing vrrp scripts before running them. We can detect they are missing from the return of the exec call. * Allow threads that don't need a timeout to never timeout * Calculate the maximum fd number when calling select() rather than specifying the maximum of 1024. * Ignore netlink NEWLINK messages that are only wireless state changes. * Don't check whether timers have expired after select() returns if its timeout didn't expire. * Termination of child processes (scripts) were being handled twice * Don't generate the IP header checksum since the kernel will always generate it. * Maintain pointers to tracking scripts to save seaching a list to find the relevant script. * Vrrp instances to have pointer to interface structure to avoid having to search based in index * Fix the checksum calculation for VRRPv3 unicast peers. * Don't regenerate the full advert packet each time an advert is sent Keepalived now simply updates the necessary fields and calculates the change needed to the checksum. * Detect a vmac interface going down, and make the vrrp instance transition to fault state. Previously the instance would only go down if the underlying interface went down. * Stop weighted track scripts updating priority of sync group members * Make vrrp instances go straight to fault state at startup if a relevant interface is down Previously an instance would start in up state and transition to fault at next timer expiry * Ensure that a sync group starts in backup state unless all members are address owners * Restore master down timer after leaving fault state * Use execve() to execute scripts rather than system(). This saves a fork and an extra process, and also allows the parameters to be parsed once only at startup, rather than each time the script is invoked. * Don't treat a failure to execute a script as a failure of the script * Ensure all scripts receive TERM signal when keepalived terminates * If keepalived is running with an elevated priority, stop running scripts with that elevated priority. * Enable an unweighted tracking script make a vrrp instance which is an address owner transition to fault state * Delay bringing vrrp instances up at startup until after the first completion of the tracking scripts This stops an instance coming up an then being brought back down again after the script completes with a failure. * Reduce number of error messages if a script is not executable * Add linkbeat option per vrrp instance * Fix timer addition on 32-bit systems * Ignore netlink messages for interfaces using linkbeat polling * If priority of vrrp instance changes when in backup due to a vrrp script, reschedule the read timeout * If re-using a VMAC after a reload, ensure it is correctly configured * Don't send priority 0 adverts when transition to fault state unless were in master mode * Identify routes added by keepalived as belonging to keepalived * Enable vrrp instances to be put into fault state if their routes are removed * Add track scripts, track files and track_if to sync groups and deprecate global_tracking (use sync_group_tracking_weight instead, but only if necessary). * Improve AH authentication sequence number handling, and (re)enable sequence number checking for VMACs and sync groups * Remove autoconf/automake generated files from git repo. Script build_setup will create the necessary build environment. * Improve and standardise notifications * Fix not sending RS and VS notifies if omega set * Add no_checker_emails to not send emails every time a checker changes state, but only if a real server changes state * Monitor VIP/eVIP deletion and transition to backup if a VIP/eVIP is removed unloes it is configured with the no-track option. 2018-05-26 Alexandre Cassen * keepalived-1.4.5 released. * Update snapcraft.yaml for 1.4.x+git * Fix generation of git-commit.h with git commit number. * Set virtual server address family correctly. * Set virtual server address family correctly when using tunnelled real servers. * Fix handling of virtual servers with no real servers at config time. * Add warning if virtual and real servers are different address families. Although normally the virtual server and real servers must have the same address family, if a real server is tunnelled, the address families can be different. However, the kernel didn't support that until 3.18, so add a check that the address families are the same if different address families are not supported by the kernel. * Send correct status in Dbus VrrpStatusChange notification. When an instance transitioned from BACKUP to FAULT, the Dbus status change message reported the old status (BACKUP) rather than the new status (FAULT). This commit attempts to resolved that. 2018-05-08 Alexandre Cassen * keepalived-1.4.4 released. * doc: ipvs schedulers update * Fix a couple of typos in configure.ac. * Fix namespace collision with musl if_ether.h. * Check if return value from read_value_block() is null before using. * Fix reporting real server stats via SNMP. * Make checker process handle RTM_NEWLINK messages with -a option Even though the checker process doesn't subscribe to RTNLGRP_LINK messages, it appears that older kernels (certainly 2.6.32) can send RTM_NEWLINK (but not RTM_DELLINK) messages. This occurs when the link is set to up state. Only the VRRP process is interested in link messages, and so the checker process doesn't do the necessary initialisation to be able to handle RTM_NEWLINK messages. This commit makes the checker process simply discard RTM_NEWLINK and RTM_DELLINK messages, rather than assuming that if it receives an RTM_NEWLINK message it must be the VRRP process. This problem was reported in issue #848 since the checker process was segfaulting when a new interface was added when the -a command line option was specified. * Fix handling RTM_NEWLINK when building without VRRP code. * Fix building on Fedora 28. net-snmp-config output can include compiler and linker flags that refer to spec files that were used to build net-snmp but may not exist on the system building keepalived. That would cause the build done by configure to test for net-snmp support to fail; in particular on a Fedora 28 system that doesn't have the redhat-rpm-config package installed. This commit checks that any spec files in the compiler and linker flags returned by net-snmp-config exist on the system building keepalived, and if not it removes the reference(s) to the spec file(s). 2018-04-09 Alexandre Cassen * keepalived-1.4.3 released. * vrrp: setting '0' as default value for ifa_flags to make gcc happy. * Add additional libraries when testing for presence of SSL_CTX_new(). It appears that some systems need -lcrypto when linking with -lssl. * Sanitise checking of libnl3 in configure.ac. * Report and handle missing '}'s in config files. * Add missing '\n' in keepalived.data output. * Stop backup taking over as master while master reloads. If a reload was initiated just before an advert, and since it took one advert interval after a reload before an advert was sent, if the reload itself took more than one advert interval, the backup could time out and take over as master. This commit makes keepalived send adverts for all instances that are master immediately before a reload, and also sends adverts immediately after a reload, thereby trippling the time available for the reload to complete. * Add route option fastopen_no_cookie and rule option l3mdev. * Fix errors in KEEPALIVED-MIB.txt. * Simplify setting on IN6_ADDR_GEN_MODE. * Cosmetic changes to keepalived(8) man page. * Don't set ipvs sync daemon to master state before becoming master If a vrrp instance which was the one specified for the ipvs sync daemon was configured with initial state master, the sync daemon was being set to master mode before the vrrp instance transitioned to master mode. This caused an error message when the vrrp instance transitioned to master and attempted to make the sync daemon go from backup to master mode. This commit stops setting the sync daemon to master mode at initialisation time, and it is set to master mode when the vrrp instance transitions to master. * Fix freeing vector which has not had any entries allocated. * Add additional mem-check disgnostics vector_alloc, vectot_alloc_slot, vector_free and alloc_strvec all call MALLOC/FREE but the functions written in the mem_check log are vector_alloc etc, not the functions that call them. This commit adds logging of the originating calling function. * Fix memory leak in parser.c. * Improve alignment of new mem-check logging. * Disable all checkers on a virtual server when ha_suspend set. Only the first checker was being disabled; this commit now disables all of them. Also, make the decision to disable a checker when starting/reloading when scheduling the checker, so that the existance of the required address can be checked. * Stop genhash segfaulting when built with --enable-mem-check. * Fix memory allocation problems in genhash. * Properly fix memory allocation problems in genhash. * Fix persistence_granularity IPv4 netmask validation. The logic test from inet_aton() appears to be inverted. * Fix segfault when checker configuration is missing expected parameter Issue #806 mentioned as an aside that "nb_get_retry" without a parameter was sigfaulting. Commit be7ae80 - "Stop segfaulting when configuration keyword is missing its parameter" missed the "hidden" uses of vector_slot() (i.e. those used via definitions in header files). This commit now updates those uses of vector_slot() to use strvec_slot() instead. * Fix compiling on Linux 2.x kernels. There were missing checks for HAVE_DECL_CLONE_NEWNET causing references to an undeclared variable if CLONE_NEWNET wasn't defined. * Improve parsing of kernel release. The kernel EXTRAVERSION can start with any character (although starting with a digit would be daft), so relax the check for it starting with a '-'. Kernels using both '+' and '.' being the first character of EXTRAVERSION have been reported. * Improve grammer. * add support for SNI in SSL_GET check. this adds a `enable_sni` parameter to SSL_GET, making sure the check passes the virtualhost in the SNI extension during SSL handshake. * Optimise setting host name for SSL_GET requests with SNI. * Allow SNI to be used with SSL_GET with OpenSSL v1.0.0 and LibreSSL. * Use configure to check for SSL_set_tlsext_host_name() Rather than checking for a specific version of the OpenSSL library (and it would also need checking the version of the LibreSSL library) let configure check for the presence of SSL_set_tlsext_host_name(). Also omit all code related to SNI of SSL_set_tlsext_host_name() is not available. * Use configure to determine available OpenSSL functionality Rather than using version numbers of the OpenSSL library to determine what functions are available, let configure determine whether the functions are supported. The also means that the same tests work for LibreSSL. * Add support for gratuitous ARPs for IP over Infiniband. * Use system header definition instead of local definition IF_HWADDR_MAX linux/netdevice.h has definition MAX_ADDR_LEN, which is 32, whereas IF_HWADDR_MAX was locally defined to be 20. Unfortunately we end up with more system header file juggling to ensure we don't have duplicate definitions. * Fix vrrp_script and check_misc scripts of type * keepalived-1.4.2 released. * Make genhash exit with exit code 1 on error. Issue #766 identified that genhash always exits with exit code 1 even if an error has occurred. * Rationalise printing of http header in genhash. * Use http header Content-Length field in HTTP_CHECK/SSL_CHECK. If a Content-Length is supplied in the http header, use that as a limit to the data length (as wget does). If the length of data received does not match the Content-Length log a warning. * Optimise parameter passing to fprintf in genhash. * Don't declare mark variable if don't have MARK socket option. * Fix sync groups with only one member. Commit c88744a0 allowed sync groups with only 1 member again, but didn't stop removing the sync group if there was only 1 member. This commit now doesn't remove sync groups with only one member. * Make track scripts work with --enable-debug config option. * Add warning if --enable-debug configure option is used. * Allow more flexibility of layout of { and } in config files. keepalived was a bit fussy about where '{'s and '}'s (braces) could be placed in terms of after the keyword, or on a line on their own. It certainly was not possible to have multiple braces on one line. This commit now provides complete flexibility of where braces are, so long as they occur in the correct order. * Make alloc_value_block() report block type if there is an error. * Simplify alloc_value_block() by using libc string functions. * Add dumping of garp delay config when using -d option. * Fix fractions of seconds for garp group garp_interval. * Make read_value_block() use alloc_value_block(). This removes quite a bit of duplication of functionality, and ensures the configuration parsing will be more consistent. * Fix build with Linux kernel headers v4.15. Linux kernel version 4.15 changed the libc/kernel headers suppression logic in a way that introduces collisions. * Add missing command line options to keepalived(8) man page. * Fix --dont-release-vrrp. On github, ushuz reported that commit 62e8455 - "Don't delete vmac interfaces before dropping multicast membership" broke --dont-release-vrrp. This commit restores the correct functionality. * Define _GNU_SOURCE for all compilation units. Rather than defining _GNU_SOURCE when needed, let configure add it to the flags passed to the C compiler, so that it is defined for all compilation units. This ensures consistence. * Fix new warnings procuded by gcc 8. * Fix dumping empty lists. Add a check in dump_list() for an empty list, and don't attempt to dump it if it is empty. * Resolve conversion-check compiler warnings. * Add missing content to installing_keepalived.rst documentation. Issue #778 identified that there was text missing at the end of the document, and that is now added. * Fix systemd service to start after network-online.target. This fix was merged downstream by RedHat in response to RHBZ #1413320. * Update INSTALL file to describe packages needed for building documentation. * INSTALL: note linux distro package that provides 'sphinx_rtd_theme' * Clear /proc/sys/net/ipv6/conf/IF/disable_ipv6 when create VMACs. An issue was identified where keepalived was reporting permission denied when attempting to add an IPv6 address to a VMAC interface. It turned out that this was because /proc/sys/net/ipv6/conf/default/disable_ipv6 was set to 1, causing IPv6 to be disables on all interfaces that keepalived created. This commit clears disable_ipv6 on any VMAC interfaces that keepalived creates if the vrrp instance is using IPv6. 2018-01-27 Alexandre Cassen * keepalived-1.4.1 released. * Improve and fix use of getopt_long(). We musn't use a long option val of 1, since getopt_long() can return that value. getopt_long() also returns longindex == 0 when there is no matching long option, and there needs to be careful checking if there is an error to work out whether a long or short option was used, which is needed for meaningful error messages. * Write assert() messages to syslog. assert()s are nasty things, but at least let's get the benefit of them, and write the messages to syslog, rather than losing them down stderr. * Enable sorry server at startup if quorum down due to alpha mode If alpha mode is configured on sufficient checkers so that a virtual server doesn't have a quorum, we need to add the sorry server at startup, otherwise it won't be added until a quorum has been achieved and subsequently lost again. In the case where some of the checkers remain in the down state at startup, this would have meant that the sorry server never got added. * For virtual servers, ensure quorum <= number of real servers If the quorum were gigher than the number of real servers, the quorum for the real server to come up could never be achieved, so if the quorum is greater than the number of real servers, reduce it to the number of real servers. * Fix some SNMP keepalived checker integer types and default values. Some virtual server and real server values were being sent to SNMP with a signed type whereas the value is unsigned, so set the type field correctly. Some virtual server and real server values that apply to checkers are set to nonsense default values in order to determine if a value has been specified. Handle these values when reporting them to SNMP replying with 0 rather than a nonsense value. * Fix some MALLOC/FREE issues with notify FIFOs. * Add instance_name/config_id to alert emails' subjects if configured. If multiple instances of keepalived are running, either different instance_names and/or config_ids, it is useful to know which keepalived instance the email relates to. * Ensure that email body string isn't unterminated. Using strncpy() needs to ensure that there is a nul termination byte, so this commits adds always writing a nul byte to the end of the buffer. * Remove duplicate fault notification. * Fix problem with scripts found via PATH with a '/' in parameters. Recent discussions on issue #101 led to discovering that if an executable without a fully qualified name was specified as a script and there was a '/' character in the parameters, then the path resolution would not work. * Send SNMP traps when go from backup to fault due to sync group. Commit 020a9ab added executing notify_fault for vrrp instances transitioning from backup to fault state due to another instance in the sync group going to fault state. This commit adds sending SNMP traps in the same circumstance. * Revert "Add instance_name/config_id to alert emails' subjects if configured". This should be handled by setting router_id * Add config option to send smtp-alerts to file rather than send emails This is useful for debugging purposes. * Add additional entry to Travis-CI build matrix. * Fix segfault if no sorry server configured for a virtual server. Issue #751 identified a segfault in vs_end_handler(), and it transpires that the forwarding method of the sorry server was being checked without first testing that a sorry server had been configured. * Improve the log message when a master receives higher priority advert. The log message reported in issue #754 "VRRP_Instance(VI_1) Received advert with higher priority 253, ours 253" is somewhat misleading since 253 == 253. This commit improves the log message in this case be reporting that the sender's IP address is higher and the priority is equal. It also states the it was a master receiving the advert. * First stage of making --enable-debug work Issue #582 identified that compiling with --enable-debug produced an executable that didn't work. This commit largely makes that option work, but there needs to be more work to make signals work. * Generalise handling of signals. * Don't assume json header files are in /usr/include/json-c Use pkg-config to find the location of the json header files when testing for the presence of the header files in configure. * Add file updated by configure.ac change. * Log more helpful message when healthchecker activated or suspended Include the realserver in the log message * Fix building with musl libc. * fix spelling mistakes about keyword promote_secondaries in man page. 2017-12-29 Alexandre Cassen * keepalived-1.4.0 released. * Add Linux build and runtime versions to -v output. * Log kernel version and build kernel version to log at startup. * Fix compiling with --enable-debug. * Don't sleep for 1 send when exiting vrrp process if no vrrp instances. * Streamline and rationalise use of child_finder function. The child_finder function is simplified, and also stop using the parent process' child_finder function in the checker process. * Don't request bug report if script terminates due to seg fault. The report_child_status() function would log a message requesting a bug report if a check_misc script or a vrrp_track script exited due to a seg fault. * Handle vrrp track and check_misc scripts being killed by signal. * Rationalise reporting of child process exit status. report_child_status() is now only called in the main keepalived program. The reporting of the exit status of vrrp track scripts and MISC_CHECK scripts is now handled in the specific code for those scripts. This means that non 0 exit statuses aren't repeatedly reported for vrrp track scripts. * eally fix reporting of child process exit status. * Log a helpful message i using mem-check and too many allocs. keepalived simply being terminated by SIGABRT with no diagnostic message was unhelpful. * Rename child_finder() to child_finder_name() etc The function only finds the name of the child process, and not the thread for the child process, so rename the function accordingly. * Add log to file and no syslog options. With large configurations the syslog can get flooded and drop output. This commit adds options to not log to syslog, and also to log all output to files. * Add option to only flush log files before forking. * Don't poll netlink for all interfaces each time add a VMAC. We can poll for the individual interface details which significantly reduces what we have to process. * Print interface details in keepalived.data output. * Be consistent with type of size parameter for mlists. * Fix sign conversion warnings. * Add high performace child finder code. The code to find the relevant thread to execute afer a child process (either a vrrp track script or a misc_check healthchecker) was doing a linear search for the matching pid, which if there are a large number of child processes running could become time consuming. The code now will enable high performance child finding, based on using mlists hashed by the pid, if there are 32 or more vrrp track scripts or misc check healthcheckers. The size of the mlist is based on the number of scripts, with a limit of 256. * Improve high performance child termination timeout code. * Fix high performance child finder cleanup code. * Preserve filename in script path name resolution. Some executables change their behaviour depending on the name by which they are invoked (e.g. /usr/sbin/pidof when it is a link to /usr/sbin/killall5). Using realpath() changes the file name part if it is a symbolic link. This commit resolves all symbolic links to directories, but leaves the file name part unaltered. It then checks the security of both the path to the link and the path to the real file. * Handle scripts names that are symbolic links properly. * Use fstatat() rather than stat() for checking script security. If we use fstatat() we can discover if a file is a symbolic link and treat it accordingly. * Fix building with kernels older than v4.4. * Fix building with --disable-lipiptc and --enable-dynamic-linking. * Fix building with --without-vrrp configure option. * Resolve unused return value warning. * Fix some RFC SNMP issues. * Attempt to fix mock builds. * Fix parsing of broadcast + and broadcast - * check_http.c: http_get_check_compare crash fixed in case of absense of digest. * Add -pie linker option. Since -fPIE is specified for the compiler, -pie should be specified for the linker. * check_http.c: http_get_check_compare crash fixed in case of absense o. * Fix use S_PATH and fchdir(). S_PATH wasn't defined until Linux 2.6.39 and fchdir() doesn't work with S_PATH until Linux 3.5 (according to open(2) man page). * Fix building with Linux versions between 2.6.39 and 3.3 Linux 2.6.39 introduced ipsets, but the kernel had some omissions from linux/netfilter/ipset/ip_set.h header file, so the libipset provided version needed to be used. Note: RedHat backported ipsets to at least 2.6.32, so the problem applied to earlier versions of RedHat Linux and Centos. * Fix segfault when parsing invalid real server. If the first real server ip address doesn't match the address family of the virtual server, then we need to skip parsing the rest of the real_server block. * Make when vs_end_handler is executed Commit 1ba7180b ('ipvs: new service option "ip_family"') added a sublevel_end_handler vs_end_handler, but this was being executed at the end of each real_server rather than after the virtual_server. This commit adds a new parser function install_root_end_handler(), and vs_end_handler is now installed using that function so that it is executed at the end of the virtual_server rather than after each real_server. * Allow tunnelled rs address family not to match vs family. The address family of a tunnelled real server does not have to match the address family of its virtual server, so we need to delay any setting of the vs address family from an rs address until the end of the real_server block, so that we know whether the forwarding method is tunnelling or not. Likewise the check of the sorry server has to be delayed until the end of the virtual server configuration (the tunnelling method may be specified after the address of the real/sorry server). The address family of a virtual server is only not determined by the virtual server configuration itself if the virtual server is defined by a fwmark and all of the real/sorry servers are tunnelled. In this case the address family cannot properly be determined from the address family of any tunnelled real servers. However, to maintain backward compatibility with configurations used prior to this commit, the address family of the virtual server will be taken from the address family of the (tunnelled) real/sorry servers if they are all the same; if they are not all the same it will default to IPv4 (this is not incompatible since previously mixed IPv4 and IPv6 real/sorry servers were not allowed, even if tunnelled). * Remove bogus warning for fwmark virtual servers. "Warning: Virtual server FWM 83: protocol specified for fwmark - protocol will be ignored" should not be given if no protocol has been specified. * Fix removing left-over addresses if keepalived aborts. * Fix use of init_state after a reload. Issue #627 identified that vrrp->init_state was being incorrectly used in vrrp_fault(), since it is modified at a reload. Instead of using init_state, we now use the configured priority of the vrrp instance, so if the vrrp instance is the address owner (priority 255) it will transition to master after leaving to fault state, otherwise it transitions to backup. * Remove init_state from vrrp structure init_state is no longer used, so remove it from the vrrp structure. Since it has been included in keepalived SNMP, it is preserved solely for reporting in SNMP requests. * Change conditional compilation _WITH_SNMP_KEEPALIVED_ to _WITH_SNMP_VRRP_ The functionality that the conditional compilation enabled was snmp vrrp functionality, so make the name more relevant. * Update error message in configure.ac. * Add more configure options to Travis build matrix. * Install additional libraries in Travis environment for new options. * Fix some problem found by Travis-ci. * Fix configure --disable-checksum-compat option. * Remove DOS file formatting from .travis.yml. * Add more configuration option to Travis builds and some build fixes. * Tidy up some code alignment. * Update openssl use to stop using deprecated functions openssl from version 1.1 deprecated certain functions that keepalived was using. This commit ceases using those functions if the version of openssl is >= 1.1. * Fix some issues identified by valgrind. Some file descriptors weren't being closed at exit, and also one or two mallocs weren't being freed. * Set pointer to NULL after FREE_PTR() unless exiting. * Allow sync groups with only 1 member, but issue a warning. * Fix building with LibreSSL version of OpenSSL. Unfortunately LibreSSL updates OPENSSL_VERSION_NUMBER, and its value is higher that OpenSSL's latest version. When checking the version number we need to check that we are not using LibreSSL (by checking whether LIBRESSL_VERSION_NUMBER is defined). LibreSSL also hasn't implemented the new functions that OpenSSL has provided to replace functions that are deprecated or it is recommended should not be used, and so if using LibreSSL the old functions need to be used. * Update genhash to stop using deprecated functions openssl functions. * Remove last few Subversion source file version Id strings. Some of the genhash source code still had Subversion Id strings, and these are now removed. * Add copyright update script. * Copyright update. * Remove outdated Version comment. * Fix update copyright script. * Include Makefile.in files in copyright update. * Add replaceable parameters in configuration files. * Fix some MALLOC/FREE issues with config parameters. * Add multiline configuration definitions. * Remove debugging messages left in lib/parser.c. * Fix a FREE error. * Fix keepalived.conf(5) man page. * Fix type in keepalived.conf(5) man page. * Suppress error message when removing leftover addresses at startup. 2017-10-21 Alexandre Cassen * keepalived-1.3.9 released. * Stop segfault if SSL context cannot be initialised. * Don't leave point to SSL data after freeing it. * Fix memory leak if duplicate SSL context values specified. * Don't initialise an SSL context if it isn't being used. * Checksum compatibility should refer to v1.3.6. * Update keepalived.spec.in for differences between Fedora and CentOS. * change hash to something more even and hash size accordingly. * also update size of hash in free_list. 2017-10-15 Alexandre Cassen * keepalived-1.3.8 released. * parser: do not exit when glob() doesn't match any files. * Use nodename as default id for conditional configuration. If the node name returned by uname() is host123.abc.de, then lines in the configuration file matching @host123 will match the conditional configuration test. This means that it is no longer necessary to specify the -i command line option if the conditional configuration string used in the configuration is the node name. * Option --i/--config-id parameter is not optional. Since the config-id defaults to the hostname, there is no point in allowing --config-id to be used without a parameter, just to mean use the hostname. * Use NULL instead of 0 for pointers in get_longopts struct. * Some minor tidying up of the new JSON output code. 1. Use SIGRTMIN+2 rather than (_SIGRTMIN + 4) 2. Don't include JSON code if not building with VRRP 3. Some code alignment fixes 4. Some conditional compilation additions * Add --signum command line option to report signal numbers. Since keepalived is starting to use real time signals, and those signal numbers are not fixes, this commit introduces a way to ask keepalived to report those signal numbers. * Stop command line option -i segfaulting. * Fix config include files when file has no directory par. When an include file name has no directory part, there is no directory to change to, so don't try to do so. * Use getcwd() malloc functionality if available. * Add support for csh brace globs in config file names. * Update documentation for config file include directive. * Use fchdir() when changing direcories while reading include files. The getcwd(3) man page recommends using open()/fchdir() rather than getcwd()/chdir() since fchdir() is guarantee to return to the previous directory even if directories have been renamed in between the first chdir and the second. It also suggests that it is faster, and saves mallocs or allocation of arrays on the stack of size PATH_MAX. * Use alloc_value_block() for vrrp_vip_handler(). * Fix whitespace error introduced in commit 9458c9b9. * Reinforce that '@' conditional config character must be 1st on line. The '@' conditional configuration character must be the very first on a configuration, meaning that there cannot even by whitespace before the '@' character. * Check whether GLOB_BRACE is supported (it is not part of POSIX.2). * When building a docker image, it appears that autoheader is required. * Fix IPVS virtual server setup with persistence. * Remove a merge conflict .rej file accidentally added to git. This commit also updates the .gitconfig file to ensure that .rej files will not be added in the future. * config synopsis - cleanup line endings and comment alignment. * conf examples - clean eol whitespace. * conf examples - clean triple line breaks. * add pair of config options used in misc_check. * clean surprise tab character. * many whitespace fixes; some missing docu added to synopsis. * config docs - apply code review markups. * Trivial updates to latest format cleaning patches. * Allow conditional configuration to work with include statements. * Allow '@' conditional configuration to be preceeded by whitespace. 2017-10-01 Alexandre Cassen * keepalived-1.3.7 released. * Allow broadcast address to be specified as '-' or '+' When configuring an ip address with a broadcast address, allow the use of - and + (like ip(8)) to clear or set the host specfic bits of the address, i.e. 10.6.23.254/16 broadcast + result in a broadcast address of 10.6.255.255 10.6.23.254/16 broadcast - results in a broadcast address of 10.6.0.0 * Change some code layout and macro/variable names * Print unicast peer addresses in /tmp/keepalived.data * Add negative conditional configuration. A configuration line starting @main will only be included if keepalived is started with option -i main. This commit adds configuration option @^main, meaning that the remainder of the configuration line will only be included if -i main was NOT specified. * Fix calculation of checksum for VRRPv3 IPv4 unicast peers. Alternate unicast peers were being sent adverts with the checksum set to 0. The reason for this was that the checksum field was not being set to 0 before the checksum calculation, hence causing the calculated checksum to be 0 for the second, fourth, sixth etc unicast peer. * Generate README from README.md. * Only declare (and use) auth variables if compiling with authentication. authtype_mismatch and auth_failure are only used if authentication is enabled. * The vrrp_t vmac flag should be a bool. * Add include guard for vrrp_print.h. * Log some additional vrrp variables. * Make checksum change backwards compatible. This commits adds the ability of keepalived to revert to using the old checksum calculation if it sees an advert that has an old checksum. This means that if an old and a new version of keepalived are working together, once the new version has seen an advert from an old version, it will drop back to using the old style checksum, and so the two keepalived instances will work together. There is a slight problem with this, though. If the old version starts when a new version is master, if will report bad checksums. This should be fine, since keepalived should discard the adverts, time out and send an advert which would make the new keepalived revert to old checksums. Unfortunately, keepalived does not completely ignore bad adverts, since it resets its master down timer, even for bad adverts, and so it never times out. However, in this scenario, there will still remain one master and all the other keepalived instances will be in backup state, and so VRRP functionality is preserved. As identified in commit bcf2936 until commit 67275d2 keepalived did not work with VRRPv3 and more than one other unicast peer, so for migration we only need to consider two unicast peers. To upgrade, first upgrade the keepalived instance that is in backup mode. This will see old checksums when it starts, and so start using old checksums. The other keepalived instance can then be upgraded and it will also see old checksums when it starts up. In order to switch to using new checksums, temporarily add the following line in the configuration of each vrrp instance that is in master state: old_unicast_checksum never and then reload the keepalived instance by sending it SIGHUP. After the master has restarted, restart the backup with a SIGHUP, and they will then be using new checksums. The temporary old_unicast_checksum never lines can now be removed from the configuration. * Add checker bind_if keyword. If a checker binds to a link local IPv6 address, the interface has to be specified. * Make DBus service name configurable. * Make --config-id option default to hostname. This is the equivalent of specifying --config-id `hostname -s`, and makes it more straightforward to deploy the same configuration to multiple hosts. * Issue warning if more than 1 dynamic misc_checker per real server. If different scripts return different exit statuses, the priority of the real server will keep changing. The solution is to combine the functionality into 1 script. * Improve DBus error handling. . Always clear errors to avoid leaks . Check for errors when emitting signals . Check for errors when registering objects * Stop test tcp_server leaving zombie processes. * Fix persistence_granularity handling 1. vs->addr.ss_family should never be used to check address family of vs since there may not be an address is using fwmark. 2. If using fwmark, the address family may not be known when parsing persistence_granularity 3. Set address family from format of persistence_granularity if not already set 4. Ensure entire string is a number and is between 1 and 128 for IPv6 5. Ensure netmask specified for IPv4 is solid * Ensure always check return from inet_stosockaddr when parsing config. * Add lthreshold and uthreshold to keepalived.SYNOPSIS. * Merge virtual server group addresses and ranges into one list. A single address can be treated as a range with only 1 address, so this reduces the number of lists that need to be processed when handling a virtual server group. A number of corrections were also made re hton/ntoh(s|l). * Remove redundant setting of real server weight. * Don't use vs->addr.ss_family for address family of virtual server. A virtual server won't use vs->addr if it is defined by a fwmark or it is uses a firtual server group. vs->af is the correct field to use. * Make ipvs_update_stats() little/big endian aware. * Simplify ipvs_update_stats(). Don't run a state machine to collect all the stats, simple iterate through the entries. * Move fetching ipvs stats into ipvs_update_vs_stats(). * Remove some #defines in ipvs_update_stats(). * Streamline setup for changing ipvs configuration. * Fix updating resolved notify script path names with parameters. * Add silent option to test/tcp_server. * Document default checker connect ip/port. * Remove duplicate setting up of file name. * Validate HTTP_GET and SSL_GET checkers. Unsure that urls have a path specified, and that the checkers have at least one url specified. * Fix memory leak if SMTP_CHECK helo_name specified. * Fix dumping of SMTP_CHECK host list. * Don't allocate and copy default SMTP_CHECK helo name unless needed. * Tidy up dumping SMTP checkers. * Remove smtp_host_t typedef. If is passed to functions that take a conn_opts_t parameter, so we need to explicitly use the correct type. * Simplify handling of host{} block in SMTP_CHECK. This also allows specifying connect_ip, connect_port as well as unsing host blocks. * Add DNS_CHECK RRSIG and DNSKEY query types. * Fix documentation re MISC_CHECK. * Detect if no misc_path specified for MISC_CHECK. If no path was specified, keepalived was segfaulting. * Add some more error messages to socket_bind_connect(). * Checker connections aren't always TCP. * Report if checker bind_if is missing If a link local IPv6 address is specified for a checker to connect to, then a bind interface must also be specified, otherwise the connect() call fails. * If a real server has inhibit_on_failure, configure it at start up If a real server had inhibit_on_failure set, but it also had an alpha mode checker, then the real server should be installed at startup with weight 0 to be consistent with what would happen if the checker had been successful but then failed. * Improve handling of virtual server groups. If multiple virtual servers use the same virtual server group, and the virtual servers have different protocols, or the virtual server groups are defined using only fwmarks and the virtual servers have different address families, then multiple versions of the entries in the virtual server groups will need to be created as IPVS virtual servers. This patch handles the creation and removal of the necessary different virtual servers for the virtual server groups. * Add virtual server protocol types SCTP and none for SNMP. * Handle virtual server with no protocol specified This is valid if fwmarks are being used. * Warn if a protocol is set on a virtual server using firewall marks. * Don't check !LIST_ISEMPTY(vs->rs) after config is validated In validate_check_config() any virtual server without any real servers is removed, so there is no need to check subsequently. * Don't allow virtual server groups without any addresses for fwmarks. * Fix and optimise handling of promote_secondaries. The promote_secondaries flag was being cleared by the first vrrp instance that stopped using an interface, rather than by the last instance. * Fix the setting of mcast address for checksum compatibility It was using INADDR_VRRP_GROUP rather than vrrp_mcast_group4. 2017-09-15 Alexandre Cassen * keepalived-1.3.6 released. * Ensure locations of pid files is consistent Issue #563 identified that the generated keepalived.service has the wrong location for the pid file. On investigating this it was discovered that keepalived isn't following the GNU coding standards for location of pid files; however, we can't now move the default location of pid files. This commit ensures that the keepalived.service file's location for pid files is consistent with where keepalived is placing them, but also adds a configure option --enable-gnu-std-paths, which means that keepalived will use ${localstatedir} for the location of pid files, while the default remains /var/run * Stop logging that preferred_lft has been set to 0. Some users are interpreting the message as a warning, and hence are unnecessarily avoiding using a /128 netmask for IPv6 addresses. The message doesn't really tell us anything useful, so remove it. * Handle not being able to load ip_tables or ip6_tables modules. When running in a docker container it isn't possible to load kernel modules, so we need to cleanly handle a failure to load the modules. * Don't segfault if unable to load ip_vs module. In a docker container it isn't possible to load a kernel module. The check code was detecting that it couldn't load the module, but the checker process, when cleaning up prior to exiting, was assuming that certain pointers had been initialised which hadn't been when an error was detected so early in the initialisation. This commit adds testing for uninitialised pointers during the exit sequence. * Fix releasing malloc'd memory for saved core pattern. * Fix memory leak when adding iptables entries. * Handle missing virtual server configuration. keepalived was segfaulting if a virtual server had no real servers configured. There were also issues of checkers running even if there was missing essential configuration from a virtual server which meant it could be set up. The problems were a virtual server group specified but it didn't exist, a virtual server group with no configuration, and a virtual server address family not match the address family of a virtual server group. * Don't attempt to remove ipsets if ipset handling not initialised. * Delay initialising IPVS until affter processing configuration. If IPVS isn't configured, there is not point in loading the ip_vs module. * Fix conditional compilation tests for _HAVE_LIBNL3_ * Make dynamic flag bool. * Don't report exit status of misc_check scripts. The result of a change in status from a misc_check script is reported by the code anyway, so to log any non-zero exit code is superfluous and annoying. * Work around conflict between kernel and libipset header files. ipset copies linux/netfilter/ipset/ip_set.h (and other) header files, producing local copies that are installed as libipset/linux_ip_set.h etc as part of the libipset development package. Unfortunately although the kernel changes the include guards when processing its source code, ipset does not, and so the duplicated header files have different include guards. This patch detects if the include guards don't match, and if so if linux/netfilter/ipset/ip_set.h is included, it defines the include guard used by libipset/linux_ip_set.h before the latter is indirectly included. * add Dockerfile. * Fix detecting default script uid/gid. * Stop segfault when keepalived can't load ip_vs module. * Add some additional docker support files and add make target docker. The configuration file installed by make install isn't ideal to run keepalived with, so add a simple keepalived.conf that will be installed into the container. Add make target docker, to build the docker image. Add docker/README to give some information about building and using containers (this is mainly so that I don't forget how to the details). * Remove a line of debugging code. * Don't complain about keepalived_script user if not needed. keepalived logged a warning every time if the keepalived_script user didn't exist. We only need that warning if there is a script that uses the default user, and an alternative defult user isn't specified. * Fix relative script path names with embedded spaces. The space wasn't being restored after resolving the path name. * Fix memory leak if notify scripts specified multiple times. * Remove some residual debugging messages. * Fix memory leak if quorum up/down scripts specified multiple times. * Use realpath() to canonicalize script names. * Fix missing PARAMETER_UNSET, which caused the global value of vrrp_higher_prio_send_advert not to be used for each VRRP instance. * Remove unused variable introduced in commit 1c5bfa29. * Fix using virtual server groups following commit 5ca36cb. * Set address port to be sequence number for virtual server group. The format_vs() function uses the virtual server address port as the sequence number of the virtual server instance using the virtual server group, so we need to set it up. * Warn if real server has no checkers when alpha mode. If a virtual server is configured with alpha mode, and a real server has no checkers, the real server will never be able to be activated, so generate an appropriate warning. * Only delete virtual server once if using a virtual server group. If multiple virtual servers are using a virtual server group, the virtual servers are defined by the virtual server group, and so they should only be deleted for the first virtual server using the group. There is still an issue that the configuration of all virtual servers using the virtual server group needs to be consistent. * Add further checks for LVS configuration. * Document additional scheduling algorithms for IPVS. * Change virtual_server_t loadbalancing_kind to forwarding_method. The variable name loadbalancing_kind didn't represent the meaning of the parameter, so change it to forwarding_method. * Add fo and ovf scheduling types to SNMP. * Only check one packet scheduling if supported. * Add lvs_method per real server. The lvs_method should be settable for each real server within a virtual server. This commit maintains existing default behaviour by using the lvs_method set against the virtual server as the default for the real servers, but adds the option to configure the lvs_method individually for each real server. * Fix type in printing config of scripts. * Convert some spaces to tabs. * logger: output timestamps to console logs. * Optimise handling of config_id in parser. * Fix some typos. * prog_type variable doesn't make sense when building a DEBUG version. The DEBUG version runs everything in a single process, and to the prog_type variable is meaningless in this case. This commit excludes the prog_type variable by conditional compilation when building a DEBUG version. * Add home, -nodad, mngtmpaddr, noprefixroute, and autojoin address flags. * Update documentation for commit cc67476. * Add notify FIFO. pull requests #568 and #587 and issue #584 have all identified that if notify scripts are run in close succession, then order if processing of those scripts is indeterminate, and this is causing systems that are monitoring the state of vrrp instances to have the wrong state. There have been various suggestions about how this should be resolved, principally along the lines that the notify scripts should be run synchronously, i.e. a notify script should not be run until the previous notify script completed. While this would work, it adds some overhead to keepalived, which currently does not monitor the exit status of notify scripts. There is a further issue with notify scripts that if a large number of events occur in rapid succession (e.g. due to an interface flapping), this can cause a large number of child processes to be created very rapidly. This commit adds an alternative method for external processes receiving notification of events. Instead of forking a script for each event, keepalived will write to a named pipe. An external process can then read the pipe to receive notification of events, and process them appropriately. This is guaranteed to deliver events in the correct order. It also has the benefit that there isn't the overhead of forking a child process for each event. * If can't get local host name, set default router_id to "[unknown]". Issue #588 reported that keepalived was segfaulting when generating an SNMP trap in strlen(global_data->router_id), which presumable is due to global_data->router_id being NULL. As a precaution set router_id to "[unknown]" if get_local_name() fails". * Implement SNMP reporting smtpServerPort. Commit 128cd24 added functionality for specifying smtp server port and commit bcb09b8a added smtpServerPort to the keepalived MIB, but no code was added to report the port. This commit adds that functionality. * Don't use PATH when executing FIFO script. The path has already been resolved as part of checking the script security, so there is no need to search the path. * Log error if unable to execute FIFO notify script. * Pass FIFO name to notify_fifo_script as parameter. * Add FIFO notify for LVS notifies. To match the FIFO notifies for VRRP, this commit adds FIFO notifies for LVS. There are now three FIFOs available, a global one that will send output for VRRP and LVS, one for VRRP only and one for LVS only. * Fix conditional compilation for --enable-debug Commit 7947247 attempted to sort out making keepalived work with --enable-debug, but unfortunately it used the wrong conditional compilation variable (DEBUG instead of _DEBUG_). This commit corrects the conditional compilation tests. * Include protocol in virtual and real server output. * Stop segfaulting if no script given for a vrrp_script. * Fix a _DEBUG_ conditional compilation test. * Fix incorrect expression in clear_services(). * Fix use htons() instead of ntohs(). * Fix bad file descriptor error at reload with no virtual servers. * Delete disabled inhibit servers at reload. * Add logging to remove sorry server at reload. * Fix bad file descriptor error at reload with no virtual servers. * Delete disabled inhibit servers at reload. * Fix thread_cancel() for timed out threads. * build: add basic .travis.yml file * README.md: rename from README. * build: add build status tag in readme file All that's needed now, is for user `acassen` to go to `https://travis-ci.org/` login with the Github account, import repos from Github, and enable build for keepalived [a checkbox/button]. * Set sorry_server's fowarding_method. * Further fix for thread_cancel() for child timeout threads. Commit ade3d699 fixed removing read and write timeout threads from the ready queue when they are cancelled. This commits adds removing child timeout threads from the ready queue too. * Fix warnings from ignoring seteuid/setegid return results. * Fix dynamic linking with early versions of libnl3 without nla_get_s32. * Updated autoconf files due to autoconf upgrade. * Fix compiling with namespace collisions in net/if.h and linux.if.h. * Update travis configuration. This commit includes the installation of development library packages, updated kernel header files, using trusty for the builds, and adding more build options. * Reinstate distributing (renamed) README.md file. * More updates for updated automake/autoconf. * Fix new warnings produced by gcc 7. * Migrate failed checkers at reload (provisional implementation). * Implement comparison of checkers. genhash: libraries to link with should be put in LDADD, not LDFLAGS. * configure.ac: fixed build on older systems, namely CentOS 6. Provide AS_VAR_COPY if missing and downgrade autoconf dep to 2.63. * Fix worng migrate of checker-id. * Set active if new failed_checkers is empty. * Fix typo in interface details printing. * Enable vmacs to work when sysctl net.ipv4.conf.all.rp_filter > 0. A number of distros now set net.ipv4.conf.all.rp_filter = 1 by default. This means that when a vrrp instance is in the master state, it cannot receive adverts sent by a higher priority master, and hence we end up with 2 masters. I tried an alternative of receiving on the base interface, but no packets that have the same source MAC address as an interface on the system (i.e. the vmac interface) get delivered to the socket. For distros such as Fedora, RHEL, CentOS, ArchLinux, all.rp_filter = 1 due to systemd commit https://github.com/systemd/systemd/commit/1836bf9e1d70240c8079e4db4312309f4f1f91fd The reason given for the commit is to work around a boot-time race condition where interfaces created before default.rp_filter is set do not get the updated default.rp_filter setting, and so the all.rp_filter setting is used to override the individual interface settings. This doesn't seem the right solution to the problem, since it prevents any interface running with rp_filter = 0, and that is what we need for vmacs. I have filed an issue report for systemd at https://github.com/systemd/systemd/issues/6282, but in the mean time we need to work around the issue. Ubuntu sets all.rp_filter=1 in /etc/sysctl.d/10-network-security.conf provided by the procps package. Debian doesn't set all.rp_filter. The only solution I have found, and I am not entirely happy with this since it has effects beyond keepalived and affects the system as a whole, is to set all.rp_filter = 0. In order to seek not to change the operation of the system, if default.rp_filter < all.rp_filter, default.rp_filter is set to all.rp_filter, thereby ensuring that any new interfaces created will take the original value of all.rp_filter. It then iterates through all existing interfaces, and {interface}.rp_filter is set to the value of all.rp_filter if {interface}.rp_filter < all.rp_filter. all.rp_filter is then set to 0. This means that all interfaces should behave in the same way as before, since the behaviour of rp_filter is defined by the maximum of {interface,all}.rp_filter, but we are not able to operate the vmac interfaces with rp_filter = 0. When keepalived exists, it restores the original settings of rp_filter if they are the same as what we set them to. * Only restore rp_filter on interfaces if same as we set them to. If rp_filter has been altered since we set it, then do not restore it to the original value. * Update files for build fix commits. Commits 2cccc97 and a932cf2 provided fixes for building on CentOS6. This commit updates genhash/Makefile.in in line with genhash/Makefile.am and adds a comment to autoconf.ac regarding when autoconf introduced support of AS_VAR_COPY. * Fix build error at when _HAVE_IPV4_DEVCONF_ was undefined. * Remove unnecessary parameter compare. * Resolve compiler warning introduced by commit 8361b11. * Remove debugging log messages added in commits 99fe626 and 6ec26e0. * Fix compiler warning and remove unwanted log messages. * Make a couple of checker variables non global. * Correct comparison for checker compare in migrate_failed_checkers. Commit 2ff6b3f changed the sense of the comparisons of checkers, but didn't make the corresponding change to checking the result. * Fix keepalived.doc(5) man page. * Add virtualhost config for real servers. Different real servers may want different virtualhost config settings. The real server virtualhost setting overrides the virtual server virtualhost setting. * Allow virtualhost to be specified per checker and per url. * Fix compiling with SNMP enabled. * Fix compiler warnings when use configure --enable-conversion-checks. * Fix an unintentional case fall-through. gcc 7 identified two case statement fall-throughs. One was intentional, but the other was a bug. The latter is now fixed, a comment is added for the former so the warning isn't generated. * Fix commit cc67476 to allow flags for static and virtual ip addresses. * Fix handling of more recent ip address flags. Recent ip address flags have exceeded 8 bits, and so the IFA_FLAGS attribute needs to be used, rather than the ifa_flags field. * Fix typo in help. 2017-03-19 Alexandre Cassen * keepalived-1.3.5 released. * Ensure nopreempt is not set if address owner. * Remove hardcoded paths from init files. * Add configure option to override system init type. * Fix some configure tests for init type. * Add support for ip rules uidrange option. This option was added in Linux 4.10. * Resolve compiler warning on 32 bit systems. There were two warnings in lib/timer.c for signed vs. unsigned comparisons on 32 bit systems. * Add missing documentation for ip rule uidrange. * Include snapcraft.yaml tar file. * Remove extraneous EXTRA_DIST directory. * Add library requirements for ArchLinux. * Allow tracking and misc_check scripts time to terminate after timeout. If a script exceeds the timeout, it is sent a SIGTERM, and then if it still doesn't terminate, it is sent a SIGKILL. The problem was that the script was only allowed 2 microseconds to terminate, whereas it should have been 2 seconds. * Fix script paths when converted to absolute path names. If a tracking or misc_check script is not specified by a fully qualified path name, but rather it is resolved via PATH, the updated patch name wasn't being saved for tracking or misc_check scripts. * Remove yet more hardcoded paths. * Make git ignore keepalived.service file. * Streamline signal handling initialisation. * Report track script name if it times out. keepalived was simply reporting that pid nnnn had timed out, which didn't give any indication of what script it was that had timed out. This patch now means that the script name will be logged rather than the pid. * Fix conditional configuration for config read via alloc_value_block(). The code for handling conditional configuration was in the wrong function. This commit move it to read_line() so all configuration is read in the context of @system_id conditional lines. * Fix compiling with --disable-vrrp. When building without vrrp, the checker process still needs to know about IP address creationg and deletion in order to allow the ha_suspend configuration option to work. * The checker process never needs to monitor interfaces. * Move vrrp_ipvs_needed() to vrrp_daemon.c. * Remove some unnecessary includes of check_data.h. * Make ha_suspend work when building without vrrp. Support of ha_suspend was only enabled when keepalived was built with vrrp support. There may be other processes that are adding and deleting ip addresses, so support of ha_suspend should be enabled when building without vrrp support. Also, the vrrp process doesn't need to call the update_checker_activity() function when addresses are added or deleted. * Don't use netlink address monitoring if not using ha_suspend. * Make --release-vips (-X) option work. 'X' was not included in the optstring for getopt_long(), and so --release-vips option was not recognised. Further, only enable VRRP and checker specific options if compiled with that functionality. * Only report added/deleted addresses if relevant to keepalived. Logs could get full of messages reporting address addition/deletion that were of no relevance to keepalived. By default, keepalived will now only report address additions/deletions with the -D option if the address is relevant to keepalived. The -a option is added to log all address additions/deletions. * Remove all #ifdef _WITH_LVS_ from checker code. If building the checker code, _WITH_LVS_ is always defined (_WITH_LVS_ means build the checker code), so there is no point testing if it is defined in any of the checker code. * Only include vrrp header files when building with vrrp and also for check. Make sure vrrp header files are only included if building with vrrp (i.e. without --disable-vrrp), and likewise only include check header files if compiling with LVS support (i.e. without --disable-lvs). * Add test/tcp_server.c for testing TCP_CHECK. * Make -a option work without ha_suspend. * Fix integer types. The correct, standard integer types are uint8_t and uint16_t, not u_int8_t nor u_int16_t (the latter being kernel types). glibc and uClibc may define the kernel-compatible types, but musl (which is standards-compliant) does not. * Fix warning when compiling without libnl. * Add including where those types are used. * Add option to not use dlopen() for libipset, but link at link time. * Remove superfluous (duplicated) block of code. * Add option for dynamic (run-time) linking to libip[46]tc. * Fix dynamic linking of libiptc without ipsets. * Check iptables/ip6tables commands available before using them. * Fix some conversion check compiler warnings. * Make configure option --disable-routes do something. * Don't link to libdl if not needed. * Fix compilation with --disable-vrrp. * Don't link to libraries not required by configuration. * Remove all authentication code if --disable-vrrp-auth specified. * Remove FALLBACK_LIBNL1 and use existing _HAVE_LIBNL1_ instead. There was no point in a separate FALLBACK_LIBNL1 since it and _HAVE_LIBNL1_ always had the same value. * Add udp functionality to tcp_server test program. * Fix check_conditional_tests script. * Add option for dynamic (run-time) linking to libxtables. * First stage of run-time linking to libnl-3. * Dynamic/static linking options of libnl/libnl-3, libip[46]tc and libipset. libnl/libnl-3, libip[46]tc and libipset can all be dynamically linked at run-time, and if they are not available, keepalived will use the alternative code which is used when the libraries cannot be linked a build time. This means that a single executable keepalived can be created that will use the libraries if they are installed on the target system, but will fall back to the alternatives if the libraries are not available. This is useful for build environments such as Buildroot which will not force optional dependencies (see pull request #540), since now keepalived can be built so as not to force the optional dependencies, but to make use of them if they are installed. * Fix building without libnl/libnl-3. * Don't allow adver_int to be rounded down to 0. * Fix creation of iptables entries on more recent kernels. On a 4.9.13 kernel iptables entries were being created with return-nomatch ! update-counters ! update-subcounters, as shown by the iptables command. Although it is not understood why these options are being added, it transpires that the problem occurs when using version one of the xt_info_set_match, but doesn't occur when using version 4 of the structure. This patch ensures that the latest version of the structure that is supported by the kernel is always used. * Fix updating /proc/sys/kernel/core_pattern. Reset file offset to beginning of file between reading the file and writing new contents. * Fix printing of smtp_server port. * Handle failure if fail dynamically to get address of a libipset function. * Be defensive in case fail to get addres of a libipset function dynamically. * Fix evaluation of library names for run-time linking. * Show failed ipset dl function. * Provide explicit DL error messages and fix autobuilt snap version. * Fix formatting of email message for CHECK_SMTP failures. The format string passed to smtp_final() can contain format specifiers so a further pass through printf is required. * Add printf format attribute to vlog_message(). * Add higher_prio_send_advert vrrp config option. There is a problem if two vrrp instances, due to becoming isolated, both become master, since they will both have sent GARP messages. Setting higher_prio_send_advert and garp_lower_priority_repeat means that if a master receives a higher priority advert, it will send its own lower priority advert before it transitions to backup. The higher priority master, on receiving a lower priority advert, will then send GARP messages, and so the ARP caches will then be correctly updated. Using the higher_prio_send_advert option may be considered not to conform to the VRRP protocol (725) to (765) in state description of RFC5798, however, since which of the two masters advertises first after they can both see each other again is random, there is a 50% chance that the lower priority instance will send an advert before the higher priority instance, so to all external observers it will appear that this is the case, or at least that the adverts overlapped. * Fix higher_prio_send_advert in lower priority master. * Load the ip_tables module if using iptables. We cannot guarantee that the ip_tables modules has been loaded, so we load it ourself if using libiptc. * Fix (cosmetic) conditional compilation test. * Fix building with --enable-libxtables-dynamic --disable-libiptc. * Enable compilation with namespaces if SYS_setns is not defined. * Fix compiling with struct xt_set_info_match_v0. * Check to libnfnetlink.h and netlink.h with libnl v1 too. * Workaround missing libraries from pkg-config --libs libiptc. Old version of libiptc don't report requirements on libip4tc and libip6tc, so check if the output from pkg-config is only -L.* -liptc and if so add -lip4tc -lip6tc. 2017-02-18 Alexandre Cassen * keepalived-1.3.4 released. * Fix generation of lib/git-commit.h when building a tagged commit. * Define GIT_DATE and GIT_YEAR when generating default git-commit.h This issue was caused by commit 5287f03 which didn't define GIT_DATE and GIT_YEAR in all circumstances. 2017-02-14 Alexandre Cassen * keepalived-1.3.3 released. * Fix unitialised use of misck_checker in script timeout. * Fix detection of no netlink being installed. * Fix conditional compilation for LIBIPVS without netlink. * Terminate child processes if parent dies. If the parent keepalived process is killed, the child processes will be orphaned and can cause problem when attempting to restart keepalived. This patch makes use of prctl with PR_SET_PDEATHSIG such that all child processes will receive SIGTERM if the parent process dies. * Ensure syslog and mem_check_log open before using them. A segfault was occuring when --enable-mem-check-log option was selected, due to attempting to write to the log file before it had been opened. It was also evident that there could be attempts to write to syslog before that had been opened too. * Fix building on Centos 7/RHEL 7 re lightweight tunnel encapsulation. RedHat have partially backported lightweight tunnel encapsulation into their kernel, but not included MPLS or ILA. We need to have conditional compilation for LWTUNNEL_ENCAP_MPLS and LWTUNNEL_ENCAP_ILA rather than just checking for RTA_ENCAP. * Update documentation for tracking scripts weight 0. weight default is 0, which means tat a failure implies a FAULT state. * Reinstate code checking module ip_vs loaded. Commit d900df2 removed a bit to much code that looked as though it wasn't doing anything, with the result that the check of whether the ip_vs module was loaded didn't occur. This commit reinstates the code for checking, and if necessary loading, the ip_vs module, but also sanitises the code slightly. * Fix some more compiler warnings. * Fix a typo in a help message in configure.ac. * sorry_server: keep sorry_server on reload. * sorry_server: set it up on start or reload if quorum is down. on start: in alpha mode. on reload: if changed, or no previous sorry_server. * Added doc for priority 4th parameter to notify script. * ipwrapper.c: make functions void if return value not used or constant. Several functions in check/ipwrapper.c were always returnung the same value, and the code calling the functions then checked and returned an error if the return value was not the value always returned. Also, for some functions returning a value the return value was never checked in the calling function. Making the functions void, and removing the if (...) makes the code easier to read, and potentially slightly more efficient. * Add snapcraft.yaml for CI build publication. * Fix missing documentation for 4th parameter of notify action. * Make builds reproducable, and copyright date reflect latest commit. Pull request #503 provided an update to facilitate reproducable builds, and also ensure that the copyright date doesn't postdate the last source code modification. Unfortunately the commits required manual updates to change the copyright year, thereby creating maintainability issues. The commit also allowed fake build dates to be specified. This commit takes an alternative approach, and takes the dates used for the copyright message and the version date from the date of the last git commit. If the code is build from within a git tree, this is straightforward. On the other hand, the code may be build from tarball, so we ensure that the lib/git-commit.h file is updated when the tarball is built, and included in the tarball. * Add option to force building without libnl/libnl-3. This option is really only for test purposes to build keepalived without libnl even though libnl is installed. * Log errors if configure IPVS with IPv6 if not using libnl. The socket interface for configurating IPVS does not support IPv6, so rather than leaving the user with the error message "Operation not supported with specified address family" give a meaningful message in the log. At configure time, a warning will also be generated stating that IPVS will not support IPv6. * Ensure IPVS address families match. Don't allow a mixture of IPv4 and IPv6 addresses in a virtual server group, or within a virtual server. * When dumping an IPVS IPv6 address range, use hex. * Log if virtual_server_group doesn't exists, or address family mismatch. If a virtual server is configured to use a virtual server group but that virtual server group doesn't exist, then log an error. Also, if the address family of the virtual server group and virtual server don't match, log an error as well. We really ought to be removing the virtual server from the configuration, but I haven't worked out how to do that yet. * Don't flag changes to automake/conf generated files as source changes. The output of keepalived -v adds a '+' if there are uncommitted changes to the source code. However, we aren't interested in changes to the autoconf or automake generated files, since these aren't really "source" files, and are only included in the git repo to allow building on systems without autoconf/automake. Further, the differences may simply be due to different versions of autotools being used. * Minor formatting updates to Sphinx documentation. * Enable configure to work with ash. * Handle sysconf() returning -1 for _SC_GETPW_R_SIZE_MAX. * Report ignoring virtual server if group specified doesn't exist. It's too difficult to remove the virtual server from the configuration, but the error will be reported in the log, and so the sysadmin should resolve the configuration. * Updated snapcraft.yaml location. * Move snapcraft and reflect master version. * Add libipset3 to snap stage packages. * Allow for keepalived to be a command in /snap/bin/ as well as a daemon. * Add 'source-type: git' to avoid dirty commit versions. * Update gitignore for clean snap commit versions. * Resolve not adding '+' to git version in snapcraft builds. This is a temporary workaround to the problem of snapcraft deleting the snap/snapcraft.yaml file from its clone of the git repo (see https://bugs.launchpad.net/snapcraft/+bug/1662388 for details). * Add cleaning of snapcraft generated directories/files. * Add support for Alpine Linux. This commit adds detection and support of the OpenRC init system. * Add details of what libraries are needed for various Linux distros. * Force recreating automake/autoconf files when building with rpm. If an autoconf/automake source file is patched as part of the rpmbuild process, then some of the autoconf/automake generated files may be regenerated, and this can cause a mismatch if the versions of autoconf/ automake on the system building the rpm don't match the versions that were used to generate the files that have been committed to git. This patch changes the keepalived.spec file to always run autoreconf -f -i to ensure the generated autoconf/automake files are aligned to the right version. 2016-11-26 Alexandre Cassen * keepalived-1.3.2 released. * Correctly handle return code from system() call. If we want to check for an exit status, WIFEXITED(ret) must be checked first. * Fix compilation where SNMP enabled. * Fix a couple of SNMP errors. The length of KEEPALIVED-MIB::version was being returned a sizeof(char *) rather than strlen(char *). VRRPv3 vrrpv3GlobalStatisticsDiscontinuityTime was being completely mishandled. * Add additional files needed to build from git without autoconf. * Don't save and restore current directory twice with config includes. * Don't recognise an executable file as a configuration file. * Allow maximum path names for configuration files. * Don't check for include file after reaching EOF. * Fix a segfault if terminating at startup do to interface not found. * notify: log error while performing set{gid,uid}. Log error message while setting goup and user before system call. Maybe we should avoid system call on error if {gid,uid} are used, would be more secure. * Don't execute a script if setuid or setgid fails. This was suggested in the comment of commit 849615d and is clearly the right (secure) thing to do. * If a script doesn't have a '/' in the name, search PATH for it. This also handles spaces in script specifications where they are parameters. * Don't allow accept when strict mode set if not address owner. This commit changes keepalived from just issuing a warning to also disable accept mode when strict mode is set. Patch submitted by levin1. * Added init_fail setting to assume failed state for vrrp_script during startup of keepalived. * When checking script security check set uid/gid bits too. Although the setuid/gid bits are ignored for scripts, they are not ignored for binary executables, and there is no point in having the bits set for scripts. So we play safe, and simply check those bits, and don't attempt to ascertain if it is a script or not. * Disable scripts that aren't executable. system() on a non-executable script will fail, so we may as well just not try executing such a script. * Exit if can't read configuration file. If we have no configuration, we have nothing to run, so exit. * Don't chdir("/") if not forking. In keepalived_main() there is a comment that the working directory is / unless keepalived is run in non-forked mode, in which case it remains the current working directory when keepalived was run. Unfortunately start_vrrp_child() and start_check_child() were executing chdir("/") regardless of whether they had been forked or not. Since the parent process does chdir("/") if it is appropriate, the children will inherit that, so they don't need to chdir() at all. * Only set umask(0) in parent process. The children inherit it from the parent, so no need to set it in the vrrp or checker child processes. * Further changes for script init state failed. * notify: use _GNU_SOURCE. Just to make compiler happy about inconsitent declaration of mempcpy and strchrnul. Just cosmetics here. 2016-11-21 Alexandre Cassen * keepalived-1.3.1 released. * Ensure lists aren't empty when checking script security. * Correctly check security of scripts with parameters, and check checker notify/quorum scripts * Check security of real/virtual server notify scripts. * Handle space in filenames appropriately when checking script security. The generic notify scripts can have spaces in their filenames, all other scripts spaced delineate parameters. 2016-11-20 Alexandre Cassen * keepalived-1.3.0 released. * Add DBus functionality to VRRP. Add new pthread off VRRP to expose DBUs service org.keepalived.Vrrp1 through a GMainLoop. Create a general /org/keepalived/Vrrp1/Vrrp DBus object and a /org/keepalived/Vrrp1/Instance/#interface#/#group# object for each VRRP instance. Interface org.keepalived.Vrrp1.Vrrp implements methods PrintData, PrintStats and signal VrrpStopped. Interface com.keepalived.Vrrp1.Instance implements method SendGarp (sends a single Gratuitous ARP from the given Instance), signal VrrpStatusChange, and properties Name and State (retrievable through calls to org.freedesktop.DBus.Properties.Get) Interface files are located at location /usr/share/dbus-1/interfaces/ A policy file, which determines who has access to the service, is located at /etc/dbus-1/system.d/ * Resolve DBus working after a reload thread_destroy_list() was closing file descriptors of read and write threads, but we wanted the DBus pipes to remain open. It transpires that closing the fds in thread_destroy_list() is unnecessary, since they are closed elsewhere anyway, so stop closing the fds in thread_destroy_list(). * Add stronger compiler warnings (-Wextra). The following bugs were discovered: check_smnp_realserver_weight() comparison if unsigned value < 0 alloc_ipaddress() comparison of unsigned == -1 and not checking return status of find_rttables_scope() correctly read_line() accessing element buf[18446744073709551615] ie. buf[2^64-1], which is the same as buf[-1]. The following improvements to the code were made: Many unused function parameters either removed or marked unused Many signed vs. unsigned comparisons In most cases variables change to be unsigned Lengths being stored in signed variables * Rationalise checking of libnl-3. * Bring generation of rpmbuild keepalived.spec file up to date The keepalived.spec file is now created to match the options passed to configure. It also detects if the system init process is systemd, upstart or the traditional SYSV init system. * Add more BuildRequires to keepalived.spec.in. * Further improvements to keepalived.spec.in for systemd systems * Change some configure.ac variable names due to using PKG_PROG_PKG_CONFIG * Fix configure.ac to make RedHat hardened rpm builds work CFLAGS, CPPFLAGS and LIBS variables were not being preserved by configure.ac, and this caused needed CFLAGS to be lost when configure was run, resulting in a build failure. This commit ensures the flags are all preserved. * Allow for automake macro AM_PROG_AR not existing. * Add support for UDP socket to layer4 library. * Add DNS checker. * Update documentation for DNS health checker. * Fix compile check for PE selection support. * Add file missing from add-dns-checker commit. * Update commits for correctly checking for IPVS_SVC_ATTR_PE_NAME. The upadted configure and lib/config.h.in weren't included in the commits, and to be consistent the comment on what Linux version introduced the feature is in configure.ac if the test exists in configure.ac * Fix conditional compilation test for FRA_OIFNAME. * Fix compilation test for IFLA_INET6_ADDR_GEN_MODE. * Fix compilation test for IPVS_DEST_ATTR_ADDR_FAMILY. * Fix compilation test for IPVS_DEST_ATTR_STATS64 and IPVS_SVC_ATTR_STATS64. * Fix compilation test for RTA_VIA. * Fix compilation test for CLONE_NEWNET for DBus. * Fix issue of overwriting the original disposition of signals. * Improve forced termination of script execution process and its offspring. * Improve propagate important signal for the script process groups. * Use argument instead of static variable. * Fix bug around the process group. * Use SIGTERM instead of SIGHUP. * Stop linking with -lipset. libipset (if used) is dynamically linked at runtime, and so keepalived shouldn't be linked with -lipset. Linking with -lipset was erroneously added when converting the build system to use automake. * Report diagnostic message if dlopen() fails. * Fix loading of ipset library when development library not installed. * Don't use ipsets with namespaces on Linux < 3.13 by default. On Linux prior to version 3.13, ipsets were not network namespace aware, so by default the use if ipsets is disabled if keepalived is running in a network namespace. Configuration keyword 'namespace_with_ipsets' enables ipset use with a network namespace on these older kernels. * Fix reporting of script exit status. * Update documentation and fix compiler warning re ipset with Linux < 3.13 * Make report_child_status() check for vrrp and checker child processes report_child_status() checks for exit status KEEPALIVED_EXIT_FATAL and KEEPALIVED_EXIT_CONFIG, but these are only relevant for the vrrp and checker child processes, and not for track scripts etc. This commit adds a check that the terminating process is the vrrp or checker process before checking those exit statuses. * Add no_accept mode for VRRPv2 and standardise VRRPv3 with it RFC3768, for VRRPv2, specifies that packets addressed to the VIPs should not be accepted, unless the router is the address owner. This commit implements not accepting the packets when running VRRPv2, but only if no_accept is specified, or running in strict mode. The reason for not making no_accept the default (which would confirm to the RFCs) is that if running IPVS, or any other service on top of the VIPs, we need to be able to accept the packets, and requiring everyone to specify accept in that case would not be reasonable. Prior to this commit, VRRPv3 was blocking packets sent to VIPs (and eVIPS), unless the vrrp instance was the address owner, or accept mode was set. This commit changes the default behaviour for VRRPv3 to make it consistent with VRRPv2 (i.e. either strict mode or no_accept needs to be specified to be conformant with RFC5978). * Tidy up logged messages if ipset initialisation fails. * Streamline MII polling. We only need to read 2 MII registers, and not 32 as was previously being done. This commit also uses the header file for field and register definitions. * Simplify bitops.h code. * Resolve warnings generated with compiler option -Dconversion. Most of the warnings were resolved by changing the data types of some variables. Others required casting, particularly where kernel interfaces are involved. There were a few instances discovered that were errors, for example comparing an unsigned int against -1, and assigning a 16 bit value to a uint8_t. This commit also adds configure options --enable-conversion-checks and --enable-force-conversion-checks, the former adds compiler option -Dconversion unless the compiler is an old version that throws up false warnings. Option --enable-force-conversion-checks adds -Dconversion even if the compiler throws up known false warnings. * Fix some minor errors/typos in doc/keepalived.conf.SYNOPSIS. * Fix keyword error in sample configuration. * Fix typo in genhash error message. * Fix address ranges for virtual server groups The handling of address ranges was only written for IPv4 addresses, and only worked on little endian systems. This commit enables IPv6 address ranges to work, and also should now work on big endian systems (but I don't have access to a big endian system to test it). Validation is added to ensure that the end of the range is after the start of the range, and that the value of the range end does not exceed 255 (for IPv4) of ffff (for IPv6). There is also some optimisation of the code, so that netmask is not set (since it isn't used by the kernel), and the port is set once only, before the loop through the addresses. * Add --enable-Werror configure option. * Add promote_secondaries keyword for vrrp_instance block. If two IPv4 VIP addresses are in the same CIDR, and the primary address is removed, then by default any other address in the same CIDR is also removed. To stop this happening, the promote_secondaries flag needs to be set on the interface. Commit e5526cf added setting the promote_secondaries option on VMAC interfaces, and stated that adding the option for non-VMAC interfaces would be added later. This commit now adds a promote_secondaries configuration option in order to set the flag on the interface. * Add reporting of promote_secondaries configuration setting. * Add conditional configuration feature It is usually the case that the configurations for keepalived for systems operating together are virtually identical, and only differ in vrrp instance priorities, router id, and unicast addresses if those are being used. It is a nuisance to have to edit one file for each server to make identical changes, so this commit adds the facility for conditional configuration entries. Any line starting with the '@' character is a conditional line. Immediately following the '@' character is a config id. The line is only included in the configuration if the config id matches the argument passed to keepalived with the -i option on the command line. For example, consider the following configuration snippet: global_defs { @main router_id main_router @backup router_id backup_router } If keepalived is started with -i main, then the router id will be main_router, if started with -i backup, then backup_router. If keepalived is started without the -i option, or -i anything else, then the above snippet will not configure any router id. * Fix building with --disable-vrrp. * Stop segfaulting when configuration keyword is missing its parameter There are many places where during configuration parsing the code assumes that if a keyword is specified that requires a parameter, then the parameter exists. If the parameter doesn't exist, then the code indexes past the end of the vector, and at best segfaults, and at worst may carry on, parsing random data. This commit adds strvec_slot() which checks for the presence of the parameter, and if configured will call a function that can handle the error. Currently this logs that the parameter is missing, with as much helpful information as it can provide, and then terminates. * Use FMT_STR_VSLOT where appropriate. * Use TIMER_HZ where appropriate. * Fix comment and error message re http write timeout. * More verbose logging on (effective) priorities pt. 2. * Change configure option --enable-snmp-keepalived to --enable-snmp-vrrp The option was enabling snmp for vrrp, not all of keepalived (the --enable-snmp option does that), so this commit renames it to reflect what it is actually doing. The --enable-snmp-keepalived option is retained but marked as obsolete. * Use AS_HELP_STRING autoconf macro. * Fix process increase * Add forcing termination of children of scripts if script times out Commits fe9638b..cebfbf5 resolved problems around forced termination of scripts if they didn't terminate within the proscribed time. During the development of the patches, it was identified that after a script had been terminated by SIGTERM, any child processes created by the script also need to be kill. This commit adds the forced termination of any such children. * Correctly handle existing VMACs on reload. Anthony Dempsey in issue #449 identified that keepalived attempts to recreate existing VMAC interfaces on a reload, and that the subsequent failure causes keepalived not to use the VMAC. This then identified further issues such as the check for an existing VMAC in netlink_link_add_vmac() didn't also check the interface a VMAC was on, and that the checks for conflicts of VMAC interface names with existing interfaces weren't sufficient. This patch builds on the patch provided by Anthony Dempsey to also resolve the additional issues identified. * Fix check of matching VRRP instances on reload. On a reload, clear_diff_vrrp() removes vrrp instances that are no longer in the configuration. The check, however, was based on vrrp instance name, which might have changed. The check is now based on VRID, address family and interface, since it is this triplet that uniquely defines a vrrp instance. * Fix clearing addresses no longer used after a reload. The address comparison was including ifa_index, but that wasn't being set up until after clear_diff_vrrp() was called. * Don't zero the mem_allocated count during reload. We want to know if there is a leak during reload, so don't zero the counter. * Ensure iptables/sets entries and ip routes/rules not lost on reload. There were several places in the code that were causing existing iptables/ipsets entries to be lost on reload, and also new entries for additional ip addresses were deleted after being added. In addition, ip rules/routes for existing entries were being removed. * Ensure GARPs/GNAs are sent after reload if VIP/eVIP addresses added. Although there have been versions of keepalived when GARPs/GNAs were sent after a reload, this was due to a bug in determining if the VRRP instance had existed before. Resolving that bug (commit aaea8a4), caused keepalived to stop sending GARPs after a reload. This commit now specifically adds code to send GARPs on a VRRP instance for all addresses on that instance. It would be better if GARPs were sent only for the added addresses, and that may be resolved in a future commit. * Use correct interface for iptables/ipset entries when not accept mode If an interface was specified for a VIP/eVIP, the iptables/ipset block if not in accept mode for link local IPV6 addresses was specifying the interface the vrrp instance was on rather than the interface the address was added to. This commit now makes the iptables/ipset entry specify the interface that the address has been added to. * Resolve "Netlink: error: message truncated" messages. On systems with a page size larger than 4096 keepalived may report: "Netlink: error: message truncated" messages This error was reported on a ppc64le in an OpenStack/Nutron environment. Ppc64le is using a 64k pages size. I found that keepalived's netlink recvmsg buffer was too small causing messages to be truncated. The size of the read buffer for the netlink socket should be based on page size however, it should not exceed 8192. See the comment in the patch. I tested the fix by creating 100 veth interfaces and verifying the errors did not return. * Use ipsets with namespaces on Linux < 3.13 if ipset names configured. The problem with using ipsets with namespaces on Linux < 3.13 is that ipsets were not namespace aware, and so all ipset entries added are global to the system, including all network namespaces. This causes problems if the default ipset names are used, but if set names have been specified, it is reasonable that they have been set to be different for each namespace, and hence there will be no clashes. The documentation is also updated for vrrp_ipsets keyword. * Don't write MEM_CHECK data to log when forked script child terminates. The mem check log file was being filled with extraneous termination information every time a forked child terminated. When a child is forked it now sets a flag to stop the termination dump. * Fix illegal syntax in configure script Indirect expansion (`${!foo}`) is a bashism, it's not POSIX-sh compatible and is not supported by common shells except Bash and ZSH! Configure script should be portable, hence strictly POSIX compliant. Moreover it has shebang /bin/sh. * Make running scripts more secure Previously, keepalived ran all scripts as root. This is potentially dangerous if a non-root user can modify the script, or has write access to any part of the path to the script. This commit does the following: 1) Adds configuration options to specify the user/group under which to run each script 2) Adds an option to set the default script user/group. If this is not set it will default to user keepalived_script if that user exists, otherwise it will default to root, as before. 3) If a script is to be executed with root privilege, report if it is writeable in any way by a non-root user. 4) Add an option enable_script_security so that any scripts failing 3) above won't be executed. 5) Report if any scripts are not executable by the relevant user. * Fix some lead tab/space issues. * Fix segfault when terminating with no notify script configured. * Fix compiler warning generated with --enable-conversion-checks. * Don't segfault if modules ip_tables or ip6_tables not loaded If either of the modules is not loaded, then don't use ip(6)tables for that address family. We could load the module, but there would be no entries pointing to the chains that we use, and so there is no point adding entries to chains that won't be traversed. * Resolve some type mismatch warnings on 32 bit systems. * Fix checking security of misc_check scripts. 2016-09-11 Alexandre Cassen * keepalived-1.2.24 released. * Declare and use default value for garp_refresh. * Update documentation for default setting of snmp_server. * Ensure old VIPs removed after reload. * Add internet network control support for IPv6. * Log startup and "already running" messages to console with --log-console. * Remove VIPs on reload if no longer in configuration. * Add internet network control support for IPv6. * Add more lvs syncd options, and various minor fixes. * Don't attempt to set packet priority for wrong IP protocol. if_setsockopt_priority() was setting SO_PRIORITY socket option regardless of whether the socket was IPv4 or IPv6. Although the setsockopt() call doesn't fail for IPv6, it doesn't do anything. Commit fc7ea83 added setting IPV6_TCLASS, again for both IPv4 and IPv6, but the setsockopt() call fails on an IPv4 socket. This commit makes keepalived only set the appropriate socket option, depending on whether it is an IPv4 or IPv6 socket. The commit also changes from using the SO_PRIORITY option for IPv4 to using the more specific IP_TOS option. * Avoid compiler warning of duplicate definition. * Add function attributes to malloc functions. * KEEPALIVED-MIB vrrpRuleIndex should be unsigned. * Allow all ip rule/route options for rules and routes. This commit adds support for all ip rule/route supported options for rules and routes (and also tunnel-id rule option not yet supported by ip rule). * Make ip rules/routes a configuration option. * Add all ip rules/routes options, and minor fixes. * Corrections for rule suppress_ifgroup. * Stop respawning children repeatedly after permanent error. Keepalived was respawning very rapidly after a permanent error, which was not useful. This commit allows the detection of certain errors and if one occurs keepalived won't respawn the child processes, but will terminate with an error message. * Remove all remaining vestiges of Linux 2.4 (and earlier) support. There was code remaining for supporting ip_vs for Linux 2.4, but the remainder of the keepalived code requires Linux >= 2.6. * Make some libipvs functions static. * Move ipvs source and include files into check/include directories. * Don't duplicate kernel definitions for IPVS code. * Remove unused code from libipvs.c. * Remove ip_vs_nl_policy.c, contents now in libipvs.c. * Add ipvs 64 bit stats. * Remove linux 2.4 code, add 64bit ipvs snmp stats, and some minor fixes. * Fix compiling without SNMP checker support. The patchset removing support for Linux 2.4 introduced a problem compiling libipvs.c when SNMP checker support wasn't enabled. * Remove those annoying "unknown keyword" messages. A slight reworking of the parsing code manages to get rid of those annoying "unknown keyword" messages which we all know are't true. * Remove IP_VS_TEMPLATE_TIMEOUT. It was removed from ipvsadm in version 1.0.4. * Remove check for MSG_TRUNC being defined. It has been defined since glibc 2.2. * Remove conditionals based on libc5. libc5 predated glibc 2.0. * Remove conditional compilation checks for defines in Linux 2.6. ETHTOOOL_GLINK, RTAX_FEATURES, RTAX_INITRWND, SIOCETHTOOL and SIOCGMIIPHY are all defined in Linux 2.6, so no longer need to be wrapped in conditional compilation checks. * Sort out checks for O_CLOEXEC. * Remove check for SA_RESTART. It existed pre Linux 2.6. * Change reporting of default snmp socket. * More updates for removing pre-Linux 2.6 code, and stop "unknown keyword" messages. * Fix adding iptables entries on Linux 4.6.3 onwards. ip[46]tables_add_rules() were allocating space for an additional struct xt_entry_match. kernel commit 13631bfc6041 added validation that all offsets and sizes are sane, and the extra struct entry_match failed that test. * Fix adding iptables entries on Linux 4.6.3 onwards. * Fix size parameter for keepalived_malloc/realloc. lib/memory.h specified the size parameter to keepalived_malloc/realloc as size_t, whereas lib/memory.c specified unsigned long. The inconsistency was complained about by the compiler on 32-bit systems. Fix memory.c to make the parameter a size_t. Change lib/memory.c and lib/memory.h to use type size_t for size variables. Use printf format specified %zu for size parameters. * Fix building without LVS or without VRRP. * Convert build system to automake. The INSTALL file gives instructions for setting up the build system using automake etc. For those without automake (and autoconf), just running configure works as before. * Convert build system to automake. * Add network namespace support. This allows multiple instances of keepalived to be run on a single system. The instances can communicate with each other as though they are running in separate systems, but they are also isolated from each other for all other purposes. See keepalived/core/namespaces.c for some example configurations and use cases. * Use atexit() for reporting malloc/free checks on termination. * Add + and git commit in -v output if uncommited changes. * Add network namespace support. * Remove some superfluous conditional compilation tests. * Poll for reflection netlink messages after adding each interface. If a large number of interfaces are added, the kernel reflection netlink socket can run out of buffers. This commit adds a poll of the kernel netlink reflection channel after adding each interface, thereby ensuring that a large queue of messages isn't built up. * Stop Netlink: Received message overrun (No buffer space available) messages. * Fix debug build since automake conversion. * Fix configuration testing for ipset support prior to Linux 3.4. * Add polling of netlink messages when entering master state. If a large number of vrrp instances enter master state simultaneously the netlink socket can run out of buffers, since the netlink socket isn't read sufficiently frequently. Adding a poll of the netlink socket after the VIPs/eVIPs are added ensures that the netlink messages are read when the become available. * Add some missing '\n's when printing the vrrp configuration. * Fix generating git-commit.h. * Ensure xmit_base not set with strict mode. * Fix detection of code changes not commited to git in git-commit.h. * Change true/false variables in global_data to bools. * Fix timer_cmp handling large differences between the two times. In a struct timeval, tv_sec is a time_t which is a long. Assigning a.tv_sec - b.tv_sec to an int caused it to overflow if the time differences were large. * Add a TIMER_NEVER value. This allows a thread to specify that it never wants to be woken on a timed basis. * Add global default_interface keyword. default_interfaces sets the default interface to use for static ipaddresses. If the system does not have an eth0, or one wants to use a different interface for several static ipaddresses, this makes the configuration simpler. It also has the potential to reduces changes required if transferring the configuration to another system. * Fix skew time for VRRPv3 with low priority and long advert interval. With a low priority and a long advert interval, the calculation of the skew time was overflowing a uint32_t. For example, with a priority of 1 and an advertisment interval of 10 seconds, the skew time was being calculated as 4288 seconds, rather than 9.96 seconds. This had the impact that the backup instance would take over an hour to transition to master. * Don't set master_adver_int from an invalid packet. * Make timeout_persistence a uint32 rather than a string. * Fix some configuration tests and compiling on old Linux version. * Improve persistence handling. Properly support persistence_granularity for IPv6. Set persistence_timeout default if granularity specified. Only support persistence engine if supported by the kernel. This commit also changes variables timeout_persistence and granularity_persistence to persistence_timeout and granularity_timeout. * Simplify a bit of indentation. * Add (commented out) code for writing stack backtrace to a file. * Free syslog_ident string after logging the free. When writing mem check entries to the log, the syslog_ident needs to be freed after the log has been written to. * Allow FREE_PTR mem check to log the proper function. Having FREE_PTR as a function meant that whenever any memory was freed by FREE_PTR() the function that was logged as freeing it was FREE_PTR itself. Changing FREE_PTR() to be a #define means that the calling function name is logged. * Fix tests of HAVE_DECL_CLONE_NEWNET. * Fix a conditional compilation test re namespaces and rename a variable. * Fix when some FREE() calls are made. * Only parse net_namespace in parent process. * Add VRRP/LVS conditional compilation around PID files. * Improve removing zombie PID files. * Add more VRRP/LVS conditional compilation. * Don't check if instance is a rotuer every time an NA is sent. keepalived was calling sysctl to check if the interface was configured as router before sending each gratuitous Neighbour Discovery advertisement. This patch now checks if the interface is routing when the instance transitions to master, and uses that for all the NA messages. * Improve mem check initialisation. * Add support for running multiple instances of keepalived. Using network namespaces allows multiple instances of keepalived to run concurrently, in different namespaces, without any collision of the pid files. This patch adds the concept of a keepalived instance name, which is then use in the pidfile name, so that multiple instances of keepalived can run in the same namespace without pid file name collisions. * Add option to write pid files to /var/run/keepalived. When using namespaces or instances, pid files are written to /var/run/keepalived. The commit adds an option for the standard pid files to use that directory. * Add keywords instance and use_pid_dir, plus sundry fixes/improvements. * Add configure option to enable stacktrace support. * Fix adding and deleting iptables rules for addresses. When keepalived was built not using ipsets, the adding and deleting of rules for addresses was including an extra xt_entry_match struct that meant that the rules could only be deleted by ithe iptables command by entry number and not be specifying the parameters. * Fix compiling without libiptc (iptables) support. * Don't log error message when trying to remove leftover iptables config. At startup keepalived attempts to remove any iptables configuration that may have been left over from a previous run. Of course the entries won't normally be there, so don't report an error if they are not found. * Fix iptables entries for accept mode, other iptables fixes, and make write_stacktrace a configure option. * Add script to setup interfaces in a network namespace. The scripts mirrors the running network interfaces that are needed for a given keepalived configuration into a network namespace (default test), so that keepalived can be run in that namespace in order to test the configuration. * Correct comments re location of network namespace pid files. * Add -s option for overriding net_namespace configuration option. * Change test/netns-test.sh -c option to -f to match keepalived. * Make netns-test.sh report interfaces that don't exist. * Remove leftover debug message. * Fix address comparison for equal priority adverts. * Streamline the specification of libraries to the linker. Most of the dynamic libraries and static libraries were being specified twice. This commit removes the duplication of all of the dynamic libraries and only duplicates core/libcore.a of the static libraries. * Fix automake files for building on Ubuntu 14.04 LTS. * Enable building with Net-SNMP on Ubuntu. * Stop compiler warning on Ubuntu. * Fix compilation with libipset on Debian wheezy. * Fix various build problems on Ubuntu 14.04 and Debian. 2016-07-11 Alexandre Cassen * keepalived-1.2.23 released. * Make malloc/free diagnostics a separate configure option. The commit adds the configure --enable-mem-check option which allows the MALLOC/FREE diagnostics to be enabled without the --enable-debug option. This means that the mem-check diagnostics can be used when running keepalived in it's normal mode with forking children for vrrp and checkers. The mem-check diagnostics are written to /tmp/Keepalived_{,vrrp,healthcheckers}_mem.PID.log The --mem-check-log configure option enables command line option -L which also writes zalloc/free details to the syslog. * Fix compilation error on 32-bit systems with mem-check enabled. * Replace one zalloc() and one free() call with MALLOC() and FREE(). This ensures that the mem-check diagnostics cover all mallocs/frees. * Fix report of malloc'd memory not being freed. * Streamline read_line(). * Resolve a segfault when reloading with vmacs. The vrrp_t entries on the vrrp_data list have pointers to an interface_t for each vrrp instance. When reloading, the interface_t items where freed, but a pointer to the old list of vrrp_t items is held in old_vrrp_data. After the new configuration is processed, clear_diff_vrrp() is called. clear_diff_vrrp() uses the interface_t pointers from the old vrrp_t entries, but the memory pointed to by the interface_t pointers has already been freed, and probably reallocated for a different use. This commit delays freeing the old interface_t items until after clear_diff_vrrp() has completed, so the interface_t pointers remain valid. * Check valid interface pointer before calling reset_interface_parameters(). Before resetting the settings on the base interface of a vmac, check that the interface_t pointer is valid. * Fix new --mem-check-log option. * Don't write parent's memory logging into children's log file. When running with mem-check output to files, the buffer from the parent process was also being written into the children's log files. The commit sets the CLOEXEC flag on the log files, and also sets the log files to be line buffered. * Fix segfault or infinite loop in thread_child_handler() after reloading. When the checker and vrrp child processes start up, memory for a thread_master_t is malloc'd and saved in master. Subsequently, launch_scheduler() is called, and that sets the parameter to be passed to the SIGCHLD handler - thread_child_handler() to the value of master, pointing to a thread_master_t. If keepalived is signalled to reload, the child processes free all malloc'd memory, and a new thread_master_t is malloc'd and saved in master. If this is not the same address as the previous thread_master_t, then the value being passed to the SIGCHLD handler is a pointer to the old thread_master_t, whereas everything else is using the new thread_master_t. If the memory used for the old thread_master_t is then returned in a subsequent malloc() call, a subsequent SIGCHLD will invoke thread_child_handler() with a pointer to memory that has now been overwritten for some other purpose, hence causing either a segfault or an infinite loop. A further consequence is that new child processes will be added to the new thread_master_t, but when thread_child_hander() is called after a child terminates, it won't find the child since it is still looking at the old thread_master_t. This commit modifies the behaviour of a reload by not releasing the old thread_master_t and then malloc'ing a new one, but rather it just reinitialises the original thread_master_t and continues using it. * Remove base_iface from struct _vrrp_ - it wasn't used. * Add configuration option to flush LVS configuration. This commit adds a global configuration option lvs_flush to flush the LVS configuration, and if not set, the configuration won't be flushed. * Add back real server when return from failure with HTTP_CHECK. If status_code wasn't specified for a url entry in the configuration then a real server would never be returned to service following a failure. The commit makes keepalived return a real server to service if no status_code is specified if the HTTP status code returned from the service is a success code (i.e. 2xx). * Avoid duplication of keyword installation in check_http.c. * Fix adding new static ip addresses after reload. Commit f23ab52, when stopping duplicate static ip routes and rules being added after a reload also stopped new static ip addresses being added. The commit reinstates adding new static ip addresses. * Fix adding static iprule/routes after a reload. * Stop segfault when configure a route with no destination address. * Fix unused global vrrp_garp_master_refresh. * fix healthchecker reload when some healthchecks are failed. 2016-06-14 Alexandre Cassen * keepalived-1.2.22 released. * vrrp: Fix build without VRRP VMAC. * Fix compilation with RFC SNMP without Keepalived SNMP. * vrrp: Update master_adver_int when receive higher priority advert when master. If VRRPv3 is being used, and a higher priority advert is received when in master mode, the master_adver_int needs to be updated when transitioning backup mode. If this isn't done, and our advert interval is less than a third of the new masters, we will time out and re-enter master mode, send an advert to which the other master will resond with a higher priority advert, causing us to go back into backup mode, until our timer expires again, and this will continue indefinitely. * vrrp: Don't send advert after receiving higher priority advert. If a master receives a higher priority advert, there is no need to send another advert, since the sender of the higher priority advert is already a master. Further, any other instance in backup mode will process our subsequent advert, and then consider the wrong system to be master, until it receives another advert from the real master. With VRRPv3, if the other master has an advert interval more than three times our advert interval, backup routers will be using our advert interval after we've sent our subsequent advert, and will then timeout before the new master sends another advert, prompting (one of) the backup routers to become a master, which will prompt the higher priority master to send an advert, the ex-backup router will then send another advert and we could end up in an endless cycle. * vrrp: Fix receiving advert from address owner when in fault state. * vrrp: When transitioning from fault state, log state change. * vrrp: Fix preempt delay when transitioning from fault state. There were two ways of leaving fault state, either by receiving a packet on the instance, or by a netlink message indication that the interface is up again. In neither case was preempt_delay considered in the code. This commit changes the way vrrp->preempt_time is used. preempt_time is now only used once a higher priority advert is received, rather than being updated every time a lower priority advert is received. vrrp->preempt_time is now also set when transitioning out of fault state. vrrp->preempt_time.tv_sec == 0 now indicates the timer is not running. * vrrp: Detect and report duplicate address owners. If more than one system is configured as an address owner (priority == 255), this would be a configuration error, and could cause unexpected behaviour. This commit ensures that the problem is reported, and sets the local instance not to be the addess owner, as a temporary workaround for the problem. * vrrp: Fix maximum number of VIPs allowe. * ipvs: Fix IPVS with IPv6 addresses. * ipvs: Don't overwrite errno by another syscall before checking errno. * ipvs: ipvswrapper.c: fix comparison. * Enable compilation with development net-snmp headers. * vrrp: Fix IPv4 vIP removal when addr matches pre-existing interface addr. For IPv4 vIPs keepalived adds a /32 to the underlying interface. If this address matches an address already configured, e.g. a /24, when this vIP is eventually removed due to a configuration change or keepalived shutdown, the original address matching the vIP, outside of keepalived's control, is removed instead. This behaviour is incorrect. The /32 added by keepalived should be the address being removed. Keepalived should not be touching any addresses it does not create. * vrrp: Check for errors when opening VRRP data and stats files. This fixes crashes when running keepalived under SELinux enforcing mode, which does not allow keepalived proccess to write to /tmp by default. * vrrp: Don't assume IPADDRESS_DEL == 0 and IPADDRESS_ADD != 0. * vrrp: Fix compilation failure. * vrrp: Fix transition to backup when receive equal priority advert from higher address. When a vrrp instance in master mode received an advert from another master that had equal priority, it wasn't comparing the addresses to determine whether it should treat the advert as higher priority, and hence the instance should fall back into backup state. When checking whether the advert is from a lower priority master, it now checks if the priorities are equal and then compares the addresses. * vrrp: Optimise address comparision when receive advert in master mode. * Optimise inet_inaddr_cmp. 2016-05-26 Alexandre Cassen * keepalived-1.2.21 released. * Install VRRP-MIB when applicable. It appears that the condition in Makefile.in for installing VRRP-MIB was using a non-existent macro, SNMP_RFC2_SUPPORT. This patch removes two conditions from Makefile.in that use undefined macros and adds a condition to install VRRP-MIB when SNMP_RFCV2_SUPPORT is set appropriately. * Check virtual route has an interface before returning ifindex to SNMP * Force git-commit.h to be updated when needed * INSTALL: Keepalived doesn't need popt anymore * INSTALL: support for 2.2 kernels is long gone. * INSTALL: fix a few typos * keepalived.conf(5) some minor improvements * man keepalived(8): some minor improvements * Add printing of smtp server port when printing global config * timeout_epilog: mark argument const. * parser: mark some function arguments as const. * terminate argv with NULL. man execvp says: "The array of pointers must be terminated by a null pointer." * ipvswrapper.c: fix comparison. * mark pidfile strings as const. * utils.c: mark some arguments a const. I left inet_stosockaddr alone for now, since it modifies the string. We should fix that, since we pass in strings which might be const and in readonly memory. * netlink_scope_n2a: mark return type as const. * vector->allocated is unsigned. * notify_script_exec: mark a few arguments as const. * vscript_print: mark string as const. * vector->allocted is unsigned. * dump_vscript: mark str as const. * Updated range for virtual_router_id and priority. * Stop segfaulting with mixed IPv4/IPv6 configuration After reporting that an ip address was of the wrong family, when the invalid address was removed from the configuration, keepalived was segfaulting, which was due to the wrong address being passed to free_list_element(). * Updated range for virtual_router_id and priority in doc/keepalived.conf.SYNOPSIS * Allow '-' characters in smtp_server hostname. * Allow smtp_server domain names with '-' characters to be parsed correctly. * Report and exit if configuration file(s) not found/readable. The configuration file is treated as a pattern, and processed using glob(). If there is no matching file, then it wasn't reading any file, and keepalived was running with no configuration. This patch adds a specific check that there is at least one matching file, and also checks that all the configuration files are readable, otherwise it reports an error and terminates. * Fix building with Linux < 3.4 when ipset development libraries installed. Prior to Linux 3.4 the ipset header files could not be included in userspace. This patch adds checking that the ipset headers files can be included, otherwise it disables using ipsets. * configure: fix macvlan detection with musl libc. * Fix compiling without macvlan support. * Bind read sockets to particular interface. Otherwise, since we use RAW sockets, we will receive IPPROTO_VRRP packets that come in on any interface. * vrrp: read_to() -> read_timeout(). Make function name less confusing. * vrrp: open_vrrp_socket() -> open_vrrp_read_socket(). An equivalent open_vrrp_send_socket() exists, therefore make the read version follow the same naming convention. * vrrp: fix uninitialized input parameter to setsockopt(). * Make most functions in vrrp_print.c static. * Enable compilation on Linux 4.5.x. Including causes a compilation failure on Linux 4.5 due to both and being included, and they have a namespace collision. As a workaround, this commit defines _LINUX_IF_H before including , to stop being included. Ugly, yes, but without editting kernel header files I can't see any other way of resolving the problem. * Fix segmentation fault when no VIPs configured. When checking the VIPs in a received packet, it wasn't correctly handling the situation when there were no VIPs configured on the VRRP instance. * Improve checking of existance and readability of config files. There was no check of the return value from glob() in read_conf_file() and check_conf_file(), so that if there were no matching files, they attempted to use the uninitialised globbuf, with globbuf.gl_pathc taking a random value. A further check has been added that the files returned are regular files. Finally, if no config file name is specified check_conf_file() is now passed the default config file name rather than null. * vrrp: update struct msghdr. The vrrp netlink code assumes an order for the members of struct msghdr. This breaks recvmsg and sendmsg with musl libc on mips64. Fix this by using designated initializers instead. * Initialise structures by field names. * Detection of priority == 0 seems to be shaded. * More verbose logging on (effective) priorities. * Log changes to effective priority made via SNMP. * vrrp: use proper interface index while handling routes. It appears current code has a small typos while handling routes trying to access route->oif where it should be route->index. * vrrp: make vrrp_set_effective_priority() accessible from snmp code. just include proper file in order to avoid compilation error. * monotonic_gettimeofday: make static. * Disable unused extract_content_length function. * utils: disable more unused functions. * utils: make inet_sockaddrtos2 static. * signal: remove unused functions. * Disable unused signal_ending() consistently with other unused code. * parser: make a bunch of stuff static. * scheduler: make a bunch of stuff static. * scheduler: disable unused thread_cancel_event(). * vector: disable unused functions. * vector: make 2 functions static. * list: disable unused function. * genhash: make some functions static. * Remove unused variable. * core: make a few functions static. * checkers: make some functions static. * vrrp_arp: make some global variables file-scope. * vrrp_ndisk.c: make 2 global variables file-scope. * vrrp: make some functions and globals static. * In get_modprobe(), close file descriptor if MALLOC fails. The sequencing of the code wasn't quite right, and so if the MALLOC had failed, the file descriptor would be left open. * Fix compilation without SOCK_CLOEXEC and SOCK_NONBLOCK. SOCK_CLOEXEC and SOCK_NONBLOCK weren't introduced until Linux 2.6.23, so for earlier kernels explicitly call fcntl(). * Don't include FIB rule/route support if kernel doesn't support it. * Enable genhash to build without SOCK_CLOEXEC. * Ignore O_CLOEXEC if not defined when opening and immediately closing file. * Allow building without --disable-fwmark if SO_MARK not defined. configure complained "No SO_MARK declaration in headers" if that was the case, but --disable-fwmark was not specified. The commit stops the error message, and just defines _WITHOUT_SO_MARK_ if SO_MARK is not defined. * Update documentation for debug option. * Add options -m and -M for producing core dumps. Many systems won't produce core dumps by default. The -m option sets the hard and soft RLIMIT_CORE values to unlimited, thereby allowing core dumps to be produced. Some systems set /proc/sys/kernel/core_pattern so that a core file is not produced, but the core image is passed to another process. The -M option overrides this so that a core file is produced, and it restores the previous setting on termination of the parent process, unless it was the parent process that abnormally terminated. * Add option to specify port of smtp_-server. * Add comment re when linux/if.h and net/if.h issue resolved upstream. * Enable building with SNMP with FIB routing support. * Exclude extraneous code when building with --disable-lvs. * Update description of location of core files. * Add support for throttling gratuitous ARPs and NAs. The commit supersedes pull request #111, and extends its functionality to also allow throttling of gratuitous NA messages (IPv6), and allows specifying the delay parameters per interface, since interfaces from the host may be connected to different switches, which require different throttling rates. * Add snmpServerPort to Keepalived MIB. * Add printing of smtp server port when printing global config. * Add aggregation of interfaces for throttling ARPs/NAs. This commit adds support for aggregating interfaces together, so that if multiple interfaces are connected to the same physical switch and the switch is limited as a whole on the rate of gratuitous ARPs/ unsolicited NAs it can process, the interfaces can be grouped together so that the limit specified is applied across them as a whole. * In free_interface_queue, don't check LIST_ISEMPTY before freeing. * Clear pointer freed by free_list(). * Make FREE_PTR() clear the pointer after freeing the memory. * Make FREE() clear pointer after memory released. Since a pointer to allocated memory mustn't be used after the memory is freed, it is safer to clear the pointer. It also means that if the pointer is subsequently used, it shoud segfault immediately rather than potentially trampling over random memory, which might be very difficult to debug. * vrrp: Improve validation of advert_int. 2016-04-02 Alexandre Cassen * keepalived-1.2.20 released. * better VERSION handling * ipvs: tcp check supports retry. New tcp check config option "retry" sets the check retry counter. If tcp check fails on an alive server, keepalived will perform another checks until n_retry counter reaches zero, or until the check succeeds. The delay between retry checks is configured by the "delay_before_retry" config option. The default value is 1 retry after 1 second. This is the same feature that already exists in HTTP checker (config option "nb_get_retry"). * check_http: retry logic is refined. Retry on every error, including timeout and connection error, but only when RS is up. This is needed to reduce rs flaps: we shut the server down only after nb_get_retry failed checks. Also, do not wait for delay_loop after a successfull check to bring the server UP. * ipvs: respect the error code of the ipvs_talk. Previously, if the IPVS reflector was unable to perform its task, it reported error through syslog and ignored it. This behavior leads to inconsistancies with quorum-handler: it is called with UP even if no RS were added into the IPVS. This could take place, for example, when there is a limit of opened filehandles and keepalived was unable to open netlink socket (it is opened on every call to the ipvs_talk). Now the check is not marked as OK unless IPVS reflector reports OK. Following successfull check will try to add an RS again. The special case errors "ENOENT on remove" and "EEXIST on add" are treated with OK result code. * ipvs: remove unused resulting error code. These functions are turned from int into void: ipvs_group_sync_entry, ipvs_group_remove_entry, ipvs_syncd_cmd. * check_http: reduce cpu usage. do MD5 calculation only when configured to do so. * timer: reduce cpu usage. timer_cmp is called too often and eats much of cpu cycles. Make the comparison more effective. Increase code re-using in monotonic_gettimeofday(). Use timer_reset_lazy() where possible to omit the excess memset() call. * scheduler: reduce CPU usage. Since threads are sorted by t->sands, we could break the cycle when not expired thread found. * ipvs: rs weight changes properly on reload. Do not remove and re-add a real_server when reloading config if its weight has changed. Just edit the existing ipvs rs entry. * ipvs: new service option "ip_family". This option explicitly specifies the address family of a fwmark IPVS service entry. Previously it was determined by the AF of the first real server. This logic is kept as a fallback when the "ip_family" option is missing. Also, now it is possible to create two different services for v4 and v6 with the same fwmark number. * make 'smtp_server' config to support domain name. * use getaddrinfo() instread of gethostbyname(). * make 'smtp_server' config to support domain name. * Added vrrp 'timeout' to synopsis. * Cleaned/fixed up KEEPALIVED-MIB, it now passes smilint * Fixed vrrp_snmp_route() - it was returning the address of the pointer instead of the IP address / network address for dst, gw, gw2, and src * SNMP fixes/cleanup. * Added support for static and virtual ip rules for use with policy based routing * Add info to set a default gateway into man and sample. * vrrp: Fix socket setup code for IPv4 multicast. if_setsockopt_mcast_if was only doing anything for IPv6 interfaces. Make it work also for IPv4 interfaces, and then don't need to call if_setsockopt_bindtodevice for multicast. Is it still necessary to call it for unicast? * vrrp: Set (and restore) interface parameters. In order to receive and send multicasts on the correct interfaces various parameters need to be set via the /proc/sys/net/ipv4/conf interface. This patch sets them as needed, and restores any changes on the underlying interface on exit. If a user currently sets any parameters by scripts, that will override these changes and still work, but this change in general will make it unnecessary to change any parameters with scripts. * vrrp: Leave VRRP multicast group by ifindex. Since we know the interface index, use that instead of the address since it is more efficient. Also, in the unlikely event that the interface doesn't have an address, then this avoids a problem. * vrrp: Don't delete vmac interfaces before dropping multicast membership. Further to commit afea07bd94384c8ac8125e8cdbfd18bc4a46b14e, the dropping multicast memberships were failing, since the vmac interfaces had already been deleted. This patch keeps the vmac interfaces until after the IP_DROP_MEMBERSHIP ioctls. Separating the sending of the VRRP priority 0 messages from the shutdown of the vrrp instances is necessary since vrrp_dispatcher_release closes the sockets that are needed for sending the messages. * vrrp: Don't open vrrp_send_socket if address family is wrong. open_vrrp_send_socket was opening a socket, and then checking that the address family was valid. Checking that the address family is valid at the beginning of the function streamlines the code. * vrrp: Stop m'cast packets being queued (and not received) on send socket. If there are other vrrp instances on the same network, their multicast packets are queued to our vrrp send socket, but since we don't receive on that socket, the messages just get queued in the kernel (run netstat -anp | grep keepalived to see the queued packets increasing). This patch clears the IP_MULTICAST_ALL option, to stop these packets being queued. * vrrp: Fix typos in log messages. * vrrp: Fix RFC reference. * vrrp: Fix vrrp parser error message. * vrrp: Add interface index to vrrp dump data. * vrrp: Don't specify source address in IP_ADD_MEMBERSHIP ioctl. If ifindex is specified, any source address given is ignored. * vrrp: If fail to remove vmac i/f, don't report success after fail message. * Help vim's formatting to work in configure.in. The single "'" in a comment confuses vim, and the screen formatting gets confused. Adding a second "'" in a C comment sorts vim out. * vrrp: Don't explicitly drop IGMP membership before interface deletion. The kernel will send IGMP leave group messages when an interface is deleted, so there is no need for us to do so. Experimentation has shown that explicity doing IGMP_DROP_MEMBERSHIP doesn't make it any more likely the IGMP leave group messages will be sent. Adding the 1 second sleep significantly increases the likelihood of the IGMP messages being sent, but is doesn't guarantee it. Extending the sleep time doesn't improve the chances. * Fix compiler warnings. * vrrp: Add info to set a default gateway into man and sample. * vrrp: Don't report error on interface creation/deletion. netlink_reflect_filter was returning an error if it didn't already know about an interface that has just been created. If we don't know about the interface, simply ignore it. Likewise on interface deletion, if we don't know about the interface, ignore it. * vrrp: Ensure the first interface's parameters are set when using libnl3. Patch 60217b63242bee37b1c97a04644be6eb5e18b4c4 sets the interface parameters for each interface, but when using libnl3 there was a conflict with libnl, causing the parameters not to be set for the first interface. This patch makes vrrp_netlink.c use libnl3 if it is available, to avoid the conflict. * vrrp: Fix interface parameter setting with libnl3 and error message on interface creation/deletion * vrrp: Allow gratuitious ARP parameters to be configured globally. It is likely that the gratuitions ARP parameters will want to be the same for all interfaces, so allow the defaults to be set globally. Also allow vrrp_garp_delay to be set to 0 to indicate not to send further garp messages after a delay (to emulate how the kernel sends gratuitous ARPs). * ipvs: Remove nat_mask configuration parameter. nat_mask was only valid with 2.2 kernel, and the implementation of it was removed in patch d51194f... but some of the configuration code remained. This patch removes all remaining code relating to nat_mask. * Update man pages. keepalived.conf.5 is updated to include all configuration parameters, and keepalived.8 is updated to document the signals that can be used with keepalived. * Remove remaining 2.2 kernel code. * vrrp: Allow specification of default VRRP version to use. Rather than have to specify using VRRP version 3 on each VRRP instance, allow global configuration to set the default version. * vrrp: Remove use of deprecated nl_join_groups(). The use of nl_join_groups was introduced in commit 84cf733.. in order to resolve quickly a problem introduced in an earlier patch. This patch follows the approach adopted by libnl3, which uses a list of groups, rather than a bitmap which is limited to 32 groups. * Documentation updates, removal of redundant code, global config. * vrrp: set router flag in neighbour advertisements. This is necessary in order to prevent the IPv6 stack on a node that receives the unsolicited and overriding neighbour advertisement for the VIP (that gets sent automatically when Keepalived transitions to MASTER state) from immediately removing the VIP from its list of default routers. See https://bugs.launchpad.net/bugs/1520517 for an example of the problems this can cause. Note that the approach in this patch simply unconditionally sets the router flag. That is better than having it unconditionally unset (VRRP stands for Virtual *Router* Redundancy Protocol, after all), but it might not be appropriate whenever VRRP is used to fail over addresses that are used for other tasks than being routers. Thus it might be better to read in the interface's "forwarding" sysctl and set the router flag accordingly, or making the value of the router flag configurable in keepalived.conf. * vrrp: Dynamic addition of interfaces from netlink msg. When a tracked interface is deleted then recreated with the same config VRRP groups tracking this interface will remain down. This is due to tracking of stale information. This patch listens for netlink messages for the creation of interfaces and does one of two things. i) If the interface doesn't exist in the vrrp interface list a new interface structure is created and the information from the message is used to fill the structure. This new interface is then added to the interface queue. ii) If the interface already exists in the queue we zero it and then use the information in the message to fill the structure. * branch to fix empty RS list issue. * a fix for services with no RS. * check: segfault when there is no real server for a virtual server. * vrrp: Stop memory leak rename function for convention. Renamed netlink_populate_intf_struct to netlink_if_link_populate to fit with file naming scheme. It was possible that a created ifp structure would not be cleaned up if netlink_if_link_populate returned a -1, fixed this so the structure is FREEd. * Make parent process handle and propagate USR1/2 signals. In order to be able to automate writing configuration and/or stats the signals USR1 and USR2 need to be able to be sent to the parent process since its pid can be read from /var/run/keepalived.pid. The parent then needs to propagate these signals to a vrrp child. * Ignore all signals except those explicitly wanted. In order to harden keepalived against a user accidentally sending a wrong signal to keepalived, set all signals other than those we want actioned to be ignored. * Remove potential race condition when setting signal handlers. There was the potential for signal_run_callback to be invoked after calling sigaction for a signal, prior to the internal signal handler signal_SIG***_handler and signal_SIG***_v variables being set up. To remove the race condition, when setting a signal handler block the signal until the internal handlers have been fully set up. * Make signal_ignore mean ignore. signal_ignore was setting a signal handler for the signal, but then itaking no action when the signal was received. This is now changed so the signal is actually set to be ignored. * Streamline signal handling code. There was some duplication of the code for signal handling, and this slight restructuring avoids the duplication and makes it simpler. * vrrp: Invoke notify scripts with the default signal disposition. It is reasonable for notify scripts to expect to be invoked with the standard signal disposition, so when first setting up signal dispositions, remember the original state so it can be restored before the notify scripts are exec'd. * Return address of previous signal handler according to SA_SIGINFO. The man page for sigaction(2) states that SA_SIGINFO is only meaningful when establishing a signal handler. This appears not to be the case, since the flag will be set in the oldact structure on return from sigaction if the previous signal handler was established using the SA_SIGINFO flag. * Invoke all scripts with the default signal disposition. Just as the change for notify scripts, it should apply to other scripts as well. * vrrp: Don't wait on script process being killed after timeout. The child_timout_thread functions send a SIGKILL to a child process that has timed out and didn't die quickly enough after sending a SIGTERM. They then wait on the process dying. The main problem is that if the waitpid is successful here, then waitpid in thread_child_handler will never be successful for the same pid, and so the entry on the child list will never be removed and the parent thread will not be marked as ready. There is also a theoretical possibility that the child process is unkillable, and so the waitpid would hang forever. * Set thread conditions before adding to list. It seems safer to set the status and type of a thread before adding it to the ready list. * Remove some code duplication re running scripts. misc_check_thread and vrrp_script_thread were virtually identical so move duplicate code into new function system_call_script in notify.c. * Fix formating of man page. * Set standard signal disposition before invoking ip(6)tables. Call signal_handler_notify before running iptables/ip6tables. Since it is now called for more than notify scripts, rename signal_handler_notify to signal_handler_script * Move common code for opening fd 0/1/2 into a function. The code for setting fd 0/1/2 to /dev/null before running a script was in several places. All the common code is moved into a function and the function called from the relevant places. It is only necessary to reopen fd 0/1/2 if keepalived is running with the --dont-fork option, since without that option the fds are already open on /dev/null. * Optimise closure of fds before invoking scripts. Every time before a script was invoked, closeall() was called, which would spin through 1024 file descriptors closing them, even though the vast majority were not open, resulting in 1024 system calls. To avoid that, open all sockets and file descriptors (except fd 0/1/2) with the CLOEXEC flag set, so that the fds will be closed by the kernel when the script is exec'd. * Simplify some IPv4/IPv6 code. Code blocks were (unnecessarily) repeated in functions which handled both IPv4 and IPv6 situations. * Fix reloading and invoking notify scripts. * Update vrrp_scheduler.c. * Converted pdf user guide to RST with Sphinx. * Added check for libnfnetlink header during the configure step. * In free_list_elements invoke the free function if it exists. * Use of LIST_ISEMPTY to check list exists causes memory leak. * Stop parse_ipaddress FREEing via pointer passed to it. parse_ipaddress FREE'd new following an error, but new could be an address passed to the function, and therefore might not be MALLOC'd memory. This commit makes the caller of parse_ipaddress free the memory if there is an error and the calling function MALLOC'd the memory. * vrrp: Add vrrp_iptables global configuration option. The iptables/ip6tables entries were always added at the end of the INPUT chain, but for many configurations this is too late in the processing. This patch allows the chain name to which rules are added to be specified, and also allows the option of specifying no rules are to be added. If a chain name is specifed, it is necessary for that chain to already exist in the iptables and/or ip6tables config, and for that chain to be called from an appropriate point in the ip(6)tables configuration. * vrrp: Add option to block outbound traffic from VIPs. Unwanted traffic to VIPs is discarded by ip(6)tables. This adds an option to also block outgoing traffic from VIPs. * vrrp: Add iptables blocks for E-VIPs just like VIPs. * vrrp: Allow unicast IPv6 Neighbour Solicits to be received. An ip6tables rule is added to allow IPv6 NAs to be received, but we also need to be able to receive NSs to respond to neighbours attempting to verify our reachability. * vrrp: Use correct MAC address for IPv6 VRRP packets. The IPv6 VRRP packets were using the MAC address of the underlying interface, rather than the MAC address of the vmac. This commit sets the correct MAC address for IPv6, and also adds the link-local address of the underlying interface to the vmac interface, so that VRRP packets can be sent from the vmac interface, thereby using the VRRP MAC address. * vrrp: Disable IPv6 on IPv4 VRRP VMAC interfaces. If IPv6 is not disabled on VMAC interfaces, an IPv6 link local address is generated based on the virtual MAC address. This is not only contrary to RFC 5798 para 7.4, but also causes duplicate address detection failure. The address also just isn't needed! * vrrp: Fix setting nlmsg_len for netlink messages. For netlink messages, nlmsg_len must always be set to an aligned length. Prior to this commit, nlmsg_len was only being aligned when a subsequent attribute was added to the list. This was fine if the length of the last attribute added was an aligned length (which had always the case), but didn't work if the last attribute added didn't have an aligned length. This patch is needed in preparation for adding an attribute which doesn't have an aligned length. * vrrp: Stop having an IPv6 link-local address added based on VMAC mac address. IPv6 link-local addresses that were based on the virtual MAC address of the VMAC interface were being added. RFC5798 para 7.4 states that this is not permitted. It also causes duplicate address detection failure, since each instance of the virtual router was configuring the same IPv6 address on the same subnet. This commit stops the offending link-local address being addied (or removes it if it can't stop it being added), and since VRRP advertisements must be sent with the virtual MAC address, but a link-local address for the interface, if a link-local address from the underlying interface exists, it is added to the VMAC interface, otherwise the MAC address of the underlying interface is used to generate a link-local address, which is then added. It wasn't until Linux 3.17 that the IFLA_INET6_ADDR_GEN_MODE netlink message was added, via which one can stop a link-local address being automatically configured. Therefore, if IFLA_INET6_ADDR_GEN_MODE is not supported, the only way to ensure that the problematic link-local address is not added is to remove it after the interface is brought up. This is not ideal, since there is a small window when the "illegal", and possibly duplicate, link-local address exists, but I haven't found any other way of doing it for pre 3.17 kernels. * vrrp: Stop sending unnecessary attributes in netlink messages. When an IPv6 virtual address was deleted, it was being reported in the log file that preferred lifetime was being set to 0, which is only relevant when the address is being added. This commit stops adding the IFA_CACHEINFO attribute when deleting addresses, and also stops adding other unnecessary attributes. * vrrp: Allocate an IPv6 link local address to VMAC if none on real interface. The physical interface than a VMAC is configured on may not have an IPv6 link local address, but we can construct one for the VMAC using the MAC interface of the underlying interface. * vrrp: Remove code allowing mixed IPv4/IPv6 addresses. If addresses of both types were configured, the receiving end would reject the packet since the count of addresses received would have been wrong since only addresses of one family can be sent, see vrrp_in_chk: if (hd->naddr != LIST_SIZE(vrrp->vip)) Since we don't want to send the addresses of the wrong family, add them to the virtual_ipaddress_excluded block rather than the virtual_ipaddress block. * vrrp: Only set router flag in Neighbour Advertisements if forwarding. * vrrp: Enforce maximum number of vips per virtual router. If there were more than one virtual_address blocks in a virtual_router block, one could add as many virtual addresses as one wanted, since it didn't check the number already read. * vrrp: Don't ignore excess virtual_address entries. If there are too many virtual_address entries, add them to the excluded block, but still give a warning message. * vrrp: Verify VRRP configuration after all configuration read. There was a lot of duplicated checking in vrrp_parser.c to ensure that configured parameters were consistent, and also a requirement to configure certain parameters before others. This checking was incomplete, and also becoming more and more complex as more configuration options were added. This commit delays a large part of the checking until after all the configuration has been read. This removes the need for options to be specified in a certain order and also for checking in multiple places whether certain combinations are valid. As a consequence of the delay in checking the configuration, the creation of the VMAC interfaces is delayed until after the checking. * vrrp: Accept is only valid for VRRPv3 * vrrp: Verify priority and init_state consistent. * vrrp: Verify password specified for authentication. * vrrp: Verify have an ip address for interface. * vrrp: xmit_base is only valid on a VMAC. * vrrp: Ensure at least one VIP is configured on a VRRP instance. This commit requires at least one VIP to be configured on a vrrp_instance. Although the code looked as though it was designed to allow 0 VIPs, not only was that a protocol violation, but also keepalived rejected any VRRPv3 packets received without any VIPs, and also any VRRPv2 with IPv6 due to the check in vrrp_in_chk() in vrrp.c. * vrrp: Generate unique default VMAC interface names. Since the virtual router ID can be duplicated both between IPv4 and IPv6, and also between different interfaces, the approach of setting a default interface name as vrrp.VRID could produce duplicate names. This commit now attempts to use vrrp.VRID, but if that already exists, then it will try vrrpN.VRID, where N starts from 1 and increases until an unused name is found (for IPv6 it tries vrrp6.VRID before vrrp1.VRID). * vrrp: Ensure necessary uniqueness of VRIDs. VRIDs must be unique for a given address family and interface. This commit ensures that there is no duplication of VRID/address family on any interface. * vrrp: Don't assign VIPs/eVIPs to the default interface. alloc_ipaddress was always setting the interface to DLFT_INT (eth0) if no dev DEVNAME was specified to a VIP/eVIP/static address. This is fine for a static address, but doesn't make sense for a VIP or eVIP, since they should be assigned to the vrrp_instance interface, unless explicitly configured otherwise. In fact, it probably doesn't make sense to specify dev DEVNAME for a VIP/eVIP, since the addresses must be assigned to the vrrp_instance interface. * If a configuration error occurs between {}, skip to end. If a configuration error occurred in a block, the parser could get confused. This commit makes the parser ignore ignore all further entries until the end of the block. * Don't allow specification of default as an address where inappropriate. The function parse_ipaddress would allow default or default6 to be specified for any address it parsed, but it doesn't makes sense in a lot of cases, so add a parameter to indicate if default is valid. * Improve checking of configured advertisement timer. * vrrp: Make sure that a VRRP instance has a name and is unique. It was possible to specify a vrrp_instance without a name. It was also possible to specify the same vrrp instance name twice. * Extra validation for reading ip addresses. * vrrp: Ensure a sync group has a name and hasn't already been specified. * vrrp: VRRP authentication is dependent on VRRPv2 not IPv4. The check for whether authentication is not dependant on IPv4, but rather VRRPv2. This check will be conducted following reading the whole configuration. * vrrp: Log error if unknown authentication type. * Check for, and handle, '{' at beginning of a block. There was no check for a '{' at the beginning of a configuration block. This commit is the start of that check, allowing it either at the end of the line with the keyword, or on a line of its own. Also, in respect of group and notification_email, for all other configuration items, the '{' could follow on a line of its own, but for configuration items using read_value_block the '{' on a line following the keyword was read as a configuration entry. * Check for, and report, unknown keywords. A misspelt keyword would have been silently ignored, potentially causing the user difficulty in understanding why his configuration wasn't working. * If an address fails to parse, ensure don't return an apparent address. When reading an address, the address family was set early on, and a subsequent failure to parse the address left the address family configured, thereby making it appear that a valid address had been read. Simply set the address family to AF_UNSPEC on a failure. * Ensure an address option has a value. There was no check that the parameter was present after a keyword, so for example : 1.2.3.4 dev would not have generated an error message, and alloc_ipaddress would have attempted to read a word after dev, which would either cause a dore dump or possibly return a parameter from a previous configuration line. This type of checking probably needs to be added elsewhere too. * Add validation of address scope. * vrrp: Don't allow group block more than once in a sync group. If a second group is configured, the first group is lost, and its malloc'd memory is also lost. * vrrp: Make sure sync groups have at least two members. If a sync group was configured with no group {} statement, or if the group statement had no entries, then keepalived would core dump. This commit rejects groups with 0 members, and also with 1 member, since it isn't a group. It also checks that a virtual_instance isn't configured in more than one sync group, and also that the group members specified exist. * The address must be the first record in an address configuration item. When an address is configured, it must be the first entry on the line. This allows options specified afterwards to know the address family, and also when reporting errors to include the address. * vrrp: Log error if IPv6 and first address is not link local. RFC5798 section 5.2.9 requires that if the protocol is IPv6, then the first address must be the link local address of the virtual router. * vrrp: Ensure that the full VRRP packet has been received in the buffer. Although afer receiving a VRRP packet, it checked that the length specified in the IP header was long enough to contain all the VRRP data, it didn't check that the data actually received was sufficiently long, so this check is added. * vrrp: Stop VIPs in same CIDR being deleted, but only when using vmac so far. If an interface has more than one IP address in the same CIDR, when the "primary" address is deleted, all the secondary addresses are also deleted, unless /proc/sys/net/ipv4/conf/IFACE/promote_secondaries is 1. This commit sets the promote_secondaries flag on vmacs. * vrrp: Make from and to for VRRP iprules use a define. "From" and "To" were being stored as words rather than converted to defined value. This made storage requirements larger and processing them more time consuming. * Don't report configuration bytes used if not _DEBUG_. If _DEBUG_ is not defined, malloc was increasing the count of memory allocated when called, but free wasn't reducing the count, and so the figure reported was meaningless. This commit completely disables the memory allocated counting and reporting if _DEBUG_ is not defined. * vrrp: Use defines for address scopes. Rather than hard coded values for address scopes, use RT_SCOPE_* * Force order of multiplication and division to avoid underflow. * Clear list pointer after freeing list. * Fix handling of active in vectors. active wasn't being consistently updated or reported for vectors. * Make functions always returning 0 void. Three functions in utils.c always returned 0, and the calling functions weren't checking the return code, since it was pointless, so the functions have been changed to be of type void. * Use struct in_addr rather than uint32_t for IPv4 address. * vrrp: Disable all VMAC configuration code if don't have VMACs. * Allow multiple spaces in quoted strings. The handling of quoted strings saved each word separated between tokens of '"'. This meant reconstructing a quoted string lost multiple spaces and was hard work. Quoted strings are now saved as the whole quoted string, without the quotes, so retrieval is much simpler. This also allows further keywords to follow the quoted string, if desired. * vrrp: Remove string length dependencies in vrrp_print. * vrrp: Stop using deprecated bcopy. * vrrp: Add vrrp_instance name to some log messages. * Optimise returning from list_element() when end of list reached. * Make free_melement a static function. * Use INET6_ADDRSTRLEN rather than hardcoded length. * Don't format log message if not going to log it. * vrrp: Add option to reduce vrrp advert address checking. By default, every received VRRP advertisement checks the advertised addresses are the same as the configured addresses, which is o(n^2). This change adds the option to check the first packet received from a master, but not to check the VIP list in subsequent adverts from the same master. * vrrp: Ensure vrrp_buffer large enough for largest possible received packet. The allocated receive buffer had size VRRP_PACKET_TEMP_LEN, which suggests that it wasn't intended as the final solution. Instead of using a fixed buffer size, the maximum MTU across all the interfaces is calculated, and the size of the vrrp_buffer allocated is the maximum MTU size. This guarantees that any VRRP packet received will fit in the buffer. * vrrp: Improved received VRRP packet checking. First check the protocol headers have been received, then before checking the overall length of the received data, check the data in the protocol headers, since this will allow more meaningful errors to be reported. For example if there was a mismatch between VRRP versions with IPv4, a length error was being reported, rather than the version mismatch. All the error messages in VRRP packet checking now include VRRP instance name, to help tracking down where the error lies. * vrrp: Remove fixed limit number of VIPs in a VRRP advert. There was an arbirtary limit of VRRP_MAX_VIP (20) VIPs for sending a VRRP advert. Now that the vrrp_buffer is sized to be able to receive any packet up to the largest MTU size, we can dynamically allow as many VIPs as will fit in a packet (which varies depending on IPv4 or IPv6). There is also an overhead checking the received addresses in an advert against the VIPs configure on the instance, but this can now be mitigated by setting skip_chk_adv_addr on the VRRP instance. * vrrp: Fix printing of vrrp tracking scripts. * vrrp: Print Last transition time in human readable form. * Disable assert statements unless _DEBUG_ is defined. * Streamline free_list_element * Remove duplication of code between free_list and free_list_elements. * vrrp: Add vrrp strict mode, enforcing VRRP compliance. The commit doesn't yet implement strict mode, but it will block 0 VIPs, unicast peers, IPV6 in VRRPv2. * vrrp: Add some strict tests. In strict mode, the following are enforced: IPv6 required VRRPv3 There must be at least one VIP per VR instance No unicast peers Must be address owner to start in MASTER mode * vrrp: Don't allow AH authentication with IPv6 and VRRPv2. Of course, the RFCs don't allow IPv6 in VRRPv2, but it is an extension supported by keepalived. * vrrp: Some minor ipsecah updates. * vrrp: Clearly identify that VRRP has subblocks of VRRP scripts. The keepalived.conf.5 man page wasn't explicit that there are VRRP script subblocks as part of the VRRP configuration, and this is now explicit. * Trivial edits to man page keepalived.conf(5). * man page remove static_rules configuration from vrrp_instance. keepalived.conf.5 man page had an entry for static_rules within the vrrp_instance blocks, and this is clearly wrong. * vrrp: Fix typo in error message when sending VRRP advert. * vrrp: Add option not to include vrrp authentication code. RFC3768 updated VRRPv2 to remove authentication in 2004. This commit adds a configure time option to exclude authentication code. * vrrp: When adding ip(6)tables entries, only specify i/f for link_local addresses. Packets to/from global address could arrive or be sent on any interface, so don't specify the interface for blocking the packets. For link local addresses, the block must relate to the specific interface. * vrrp: Add ability to use libiptc rather than invoking ip(6)tables. Invoking ip(6)tables has a high overhead, since the process has to be forked and exec'd, and then it has to read the whole ip(6)tables filter chain before it makes a single update and commits it back. Using libiptc avoids the overhead of multiple forks/execs, and also means that multiple entries can be added/deleted to/from the ip(6)tables configuration in a single update. * vrrp: Add option to use ipsets instead of iptables to block addresses. Instead of having lists of addresses in iptables, it is much more efficient to use ipsets to handle those addresses, since that is what it is designed for. * Use /proc/sys/kernel/modprobe to find modprobe. * Reinstate SIGCHLD before forking to exec modprobe for ip_vs. The fork of modprobe to load ip_vs would have reported a failure even though it would have succeeded. * Reinstate SIGCHLD before forking to exec modprobe for ip_vs. The fork of modprobe to load ip_vs would have reported a failure even though it would have succeeded. * Fix forking/execing re closing signal pipe. When calling scripts, we don't want to give them access to the signal pipe used between the parent process and the vrrp process. * vrrp: Fix compile error when net/if.h and netlink/route/link.h conflict. Some versions of libnl3 netlink/route/link.h conflict with some versions of kernel header file net/if.h. This commit has a workaround for when there is a conflict. * vrrp: Fix compile failure with old kernels and libnl3. Issue #215 identified a compile error with pre 3.13 kernels when libnl3 was installed. This commit adds a test for that situation and avoids using rtnl_link_inet_[sg]et_conf. I haven't been able to test this on a re 3.13 kernel, but I have simulated the scenario and it compiles as expected. * vrrp: Fix compilation when ipsets not installed. * vrrp: Fix build breakage when not using libiptc. * vrrp: Fix VRRP respawning when no VIPs specified. Commit b46dec58fa failed to check the the VIP list existed before checking how many entries were in the list. This commit also defaults the address family to IPv4 if no VIPs are specified. * vrrp: Make dependency on libnfnetlink/libnfnetlink.h conditional. * Streamline handling of daemon mode flags. * Improve handling of not being able to read a pid file. If a pid file was opened, but for some reason a pid could not successfully be read, the pid used to check if a process was running was random. * Remove unused pid filename definitions. * Change outstanding debug flag tests to use bitops helpers. * Allow for different sizes of long ints in bitops. * vrrp: Ensure conversions of vrrp->adver_int etc don't overflow. * Use bitops with daemon_mode. * vrrp: Fix ip_rule direction for SNMP. Commit 2da11f99 introduced defines for ip_rule directions rather than using strings, but the commit omitted to update the snmp code when processing the directions. * add a line about the 'include' keyword in keepalived.conf(5). * fix HTTP_GET config dump. The config dumper routine dump_http_get_check was always printing the last configured checker's connection info. * dump_conn_opts: prototype change. pass the conn_opts_t pointer as a void* parameter to make the function prototype a valid dump callbac This makes smtp_dump_host() function needless, it is removed. * fix build issues on older systems. Try to avoid the build error on systems which lack of O_CLOEXEC and IP_MULTICAST_ALL defines (such as Ubuntu lucid and Debian squeeze). * Fix compilation with --disable-vrrp-auth * vrrp: Remove state VRRP_STATE_LEAVE_MASTER since it isn't used. * vrrp: Fix VRRPv2 authentication issues. * Don't redefine _GNU_SOURCE. * vrrp: Exclude function vrrp_ah_sync when --disable_vrrp_auth. * Fix some conditional compilation errors. * Streamline getopt_long options. * Remove '\n's from log messages. * Ensure standard configure generated defines are used. The defines used in the compiles in the various subdirectories were specified in each Makefile.in which could lead to inconsistencies. This commit defines APP_DEFS in configure.in, which is then used in each Makefile.in. * Dump keywords to file rather than stdout. * Add copyright message and build options to version output. This commit also ensures that the end year of the copyright date range is the current year when keepalived was built. * Stop erroneously logging error message for unknown keywords. When vrrp_parser parsed the configuration file, it didn't know about the checker keywords, and vice versa, and so reported errors. This commits makes the other keywords known but marked as inactive. * vrrp: Fix SNMP trap NewMaster. The trap must only be triggered for IPv4, since RFC2787 doesn't understand IPv6. Also, RFC2787 only supports VRRPv2 instances, so don't raise the trap for VRRPv3 instances. The IP address returned must be the actual IPv4 address, and not the ip_address_t that holds the address. * vrrp: Use underlying interface for ifindex in NewMaster traps for vmacs. If the VMAC ifindex is returned, then there is no indication that multiple VRRP instances are operating on the same physical interface, so return the ifindex of the underlying interface. This will also mean that the same ifindex should be maintained between different invocations of keepalived. * vrrp: Move SNMP private defines into vrrp_snmp.c/check_snmp.c. The defines for the net-snmp "magic" were in the header files which were included by other modules. The defines are private to the c source file, so move the defines into them, to avoid polution compilation units which included vrrp_snmp.h/check_snmp.h. * Use definition for 1.3.6.1.2.1. * vrrp: Start SNMP after reading configuration. If SNMP is started before the configuration is read, a meaningless response will be returned to net-snmp, so don't start the snmp agent until after all the config has been read. * vrrp: Fix setting SNMPv2-MIB::sysORID entries in ORTable. The length of the OID passed to register_sysORTable was wrong. * vrrp: Allow SNMP agent to unregister cleanly with more than one MIB. Separate snmp_unregister_mib() out from snmp_agent_close() to allow multiple MIBs to be unregistered before the snmp agent is closed. * vrrp: Don't register the global_oid with SNMP twice. If SNMP is enabled, both the checker process and the vrrp process were registering the global_oid. This commit makes the checker process register it if it is running, otherwise the vrrp thread registers it. * vrrp: Add read-only support for RFC2787 SNMP (VRRPv2). * vrrp: Allow any combination of keepalived and RFC SNMP support. * Allow enabling snmp via config file. * ipvs: sctp ad persistent engine support. * Fix building with --disable-lvs * Stop autoconf complaining. * vrrp: Use defined value for maximum VRRP priority. * vrrp: Simplify scheduler code vrrp_leave_fault(). Two pairs of code blocks were repeated, and each pair could be reduced to occuring only once if the conditions were merged. * vrrp: If VRRP priority is 255 and not nopreempt, configure like state MASTER. * vrrp: Ensure number of VIPs doesn't exceed 255 per instance. * vrrp: Don't check second time if IFLA_IFNAME is NULL. * Dump interface details with rest of config. * vrrp: When becoming master, block addresses before adding them. If not accept mode, entries are added to iptables/ipsets to block traffic to the VIPs/eVIPS. These entries should be added BEFORE the addresses themselves are added, to ensure there isn't a (small) window when we might reply from the added addresses. * vrrp: Document virtual_rules. * Fix memory leak re some uses of ipaddresstos(). * Fix parsing ipset names. * vrrp: Improve and fix finding vmacs left over from previous invocation. When netlink reports a new or existing interface, we can extract information that allows us to determine if the interface is a macvlan, and the type (e.g. private). We can then save that in the interface_t structure, setting the vlan flag, and base ifindex. When working out the interface name to use for VMAC instances, we can then check the interfaces which are macvlans to see if any of them match the vrrp instance in terms of mac address, underlying interface and inet address family, and if so we can then reuse the macvlan interface. Commit 9ae463e7f broke the finding of existing interfaces where the configuration didn't specify the VMAC interface name, and simply created a new interface. This commits now resolves that. There is still an issue that if an interface was in MASTER mode when keepalived terminated, when keepalived restarts it leaves the VIPs and eVIPS on the interfaces, meaning that keepalived cannot receive VRRP packets on the interface from the VRRP instance that has taken over, and it also means that there are duplicate IP addresses on the network. Another commit will resolve this issue. * vrrp: Remove ip addresses left over from previous failure. If keepalived terminates unexpectedly, for any instances for which it was master, it leaves ip addresses configured on the interfaces. When keepalived restarts, if it starts in backup mode, the addresses must be removed. In addition, any iptables/ipsets entries added for !accept_mode must also be removed, in order to avoid multiple entries being created in iptables. This commit removes any addresses and iptables/ipsets configuration for any interfaces that exist when iptables starts up. If keepalived shut down cleanly, that will only be for non-vmac interfaces, but if it terminated unexpectedly, it can also be for any left-over vmacs. * Sort out extraneous space and tab characters. The commit removes spaces followed by tabs, trailing spaces and tabs, and replaces occurrences of 8 spaces within tabs, except where the spaces and or tabs occur within strings. This has the benefit that if blocks of code are copied, git does not complain when running git am on a file produced by git format-patch. * vrrp: Simplify RFC SNMP code. The code was checking VRRP version unnecessarily, and also had code to return an index element which is not necessary. * vrrp: Don't send traps for SNMP MIBS which are not enabled. * vrrp: Don't register SNMP global OID if not handling it. If neither the checker nor the vrrp components of KEEPALIVED-MIB are enabled, don't register the global OID. * Parameters passed to traps don't need to be static. * Fix --without-lvs and --without-vrrp configure options. * Ensure general MIB is enabled if --disable-lvs configured * Avoid compiler warning re function definition to prototype. * Add RFC6527 SNMP (VRRPv3). This commit adds read-only and notifiction support for SNMP for VRRPv3 in accordance with RFC6527. * vrrp: Fix MAC address for IPv4 VMACs created after IPv6 VMACs. * vrrp: Allow routes and rules to use tables >= 256 * Don't recompile libipvs-2.6/*.c every build. * vrrp: Remove left over ip rules and routes at startup. * vrrp: Ensure ip routes added before rules, and vice versa. If ip rules are added before routes, then it is possible for a packet to be routed while the routing table is only partially complete. Adding the rule after the routes ensures that the routing table won't be processed until it is completely set up. Likewise, when removing rules and routes, remove the rules first. * vrrp: Add missing reason message for rejected VRRP packet. Issue #255 show a log identifying bogus VRRP received, but there was no reason shown for the rejection. The only instance I can find for this is if vrr->family is neither AF_INET or AF_INET6, which I think must be a bug in the code parsing and setting up VRRP instances. This commit just adds a log message to be explicit about why the packet is rejected, and also reports the value of vrrp->family. * Reduce number of calls to getaddrinfo() reducing DNS lookups. * Report if vrrp or checker process abnormally terminates. * Add option to increase child process priorities and make non swappable. * Make vrrp_daemon.c and check_daemon.c use header file for externs. * Add reporting ops mode, and minor tidying up of virtual_server config. * vrrp: Don't overwrite real interface MAC address with VMAC MAC address. When a VMAC was being created, the MAC address of the VMAC was being copied to the MAC address of the underlying interface in the interface_t structure. The netlink reflector sets up the MAC address of the new VMAC interface, so there is no need to copy a MAC address at all. * vrrp: Stop keepalived_vrrp terminating with SIGSEGV if lvs_syncd_if set. ipvs_stop() was being called before shutdown_vrrp_instances(), and so if lvs_syncd_if had been specified on a vrrp instance, keepalived would subsequently terminate with a SIGSEGV in free_interface_queue(). * Make lvs_sync_daemon global config rather than vrrp specific. * Stop lvs sync daemons on restart in case of prior abnormal termination. * Remove any residual ipvs configuration on restart. * vrrp: Optimise clear_diff_vrrp_*() functions. * Check MALLOC returned non NULL before copying to the location. * Allxoow specifying syncid for lvs syncd. * vrrp: Send second set of GARP messages afer receiving lower prio advert. When a VRRP instance transitions to master state, if garp_master_delay is non-zero, a second set of garp_master_repeat messages is sent after garp_master_delay seconds (unless 0). However, if a lower priority advert is received, keepalived didn't send a second set. This commit sends a second set if a second set would have been sent after transition to master. * vrrp: Allow setting of graduitius ARP parameters for lower prio adv separately. * Don't log a "keepalived stopped" message if keepalived already running. * vrrp: Add support for iprule and iproute table names. * Resolve MALLOC/FREE issues to iprule/iproute table names. * Make keepalived_malloc return void* to match malloc. * When reporting MALLOC/FREE status on exit, report max MALLOC'd memory. * Make libipvs use MALLOC/FREE. * Don't restore original signal state when reloading checker config. * Ensure signals USR1 and USR2 are set to ignore in checker process. * vrrp: Only free list of iprule table names if list assigned. * vrrp: Fix strict mode of vrrp instance overriding global vrrp_strict. * Attempt to fix build breakage introduced in commit 85f81dd. * Fix parsing of scope for ip addresses. * Free global ssl context on reload. * Free request_t buffer and ssl data on reload. * vrrp: Restore sync-state after reload. Currently the sync state is rebuilt from the member states after config reload. This changes now reloads the previous sync state after reload, and then pushes this back to the group members. If a new group member is added during the reload, then the new group will accept the sync group state. If a group member is removed during a reload, then a special case will be executed to force the sync-group state to BACKUP. This is required so that an alternative backup peer for the removed group is given an opportunity to take over the gateway. 2015-07-07 Alexandre Cassen * keepalived-1.2.19 released. * vrrp: fix checksum computation in vrrp v2 for socket family AF_INET One of difference between VRRPv2 and VRRPv3 is the way checksum is computed. In VRRPv2 no accumulation is specified in RFC while in VRRPv3 it uses regular accumulator with upper pseudo header. This fix restore compliant VRRPv2 for AF_INET vrrp instance. Since IPv6 socket are using IPV6_CHECKSUM option this means that checksum for VRRPv6 instance runing in native_ipv6 mode are broken. But since this is a end to end sanity check and both side are operating the same way this OK, no "compliant with VRRPv3 RFC", but anyway using native IPv6 on VRRPv2 is not really compliant too ;) * Some cosmetics at Makefile stuff. 2015-06-30 Alexandre Cassen * keepalived-1.2.18 released. * some cosmetics changes (in memory and parser). * remove dead/not used code. * revert notify script brought by last release. * revert VRRP preemption speed up extension. * vrrp: ix vrrp removes incorrect IPv4 address when VIPs are removed. * vrrp: Re-enable VRRPv2 checksum on inbound pkts. 2015-05-31 Alexandre Cassen * keepalived-1.2.17 released. * zalloc use xalloc for consistency. * memory: fix wrong size calculation in zfree. * Fix keepalived snmp configuration. * Change comments to match kernel style. * smtp: Fix wrong algorithm in RCPT-TO building. * vrrp: ICMPv6 : modify the way we copy the src address into the IPv6 header, in order to not overwrite the header' and the 'hop limit' fields * vrrp: sync status flag (up/down) for _all_ VMAC interfaces. When using VMAC and running multiple instances on the same interface, only one of the VMAC interfaces will get its status flag synched. This commit will update the status flag for _all_ VMAC interfaces attached to a base interface. * ipvs: fix segfault crash when parsing SMTP_CHECK config * ipvs: SMTP_CHECK now respects configured RS port. Before that it always used the default port 25. * ipvs: config parser: handler for the end of block. new function install_sublevel_end_handler(handler). * ipvs: new log function vlog_message taking varg_list. log_message now uses format gcc attribute, not the macro wrapper. * ipvs: bug: check_smtp was logging "#30" instead of RS address do not do nested va_start/va_end calls in smtp_final. * ipvs: clarify snmp_check config syntax. Now host{} section is optional, and all the standard connection options are available in the SNMP_CHECK{} level, too. If one or many host section persist, those base-level options are used to specify default values that can be overriden in a host section. * vrrp: Use literal constants for bit flags Use literal constants for bit flags of the "debug" global variable Change from using numeric constants to literal constants for the bit flags of the "debug" global variable. * vrrp: Backup obtains VIP resulting in a duplicate IP. VRRP backup obtains VIP resulting in a duplicate IP situation. When a priority change to the configuration of a Master router drops its priority to below that of a backup router, the VIP is not released on the Master router leading to a duplicate IP situation. * vrrp: Make preempt_delay work more than once. * vrrp: Changes needed to support AH auth in VMAC mode. Note according to the RFC this is not a requirement, but we think that our customers will expect it to work. The RFC actually discourages its use because it adds little to no additional security. We are still able to interoperate in RFC mode by not enabling authentication. * vrrp: Check VRRP header in the IP auth header is correct. In the middle of vrrp_in_chk, the existing VRRP packet parsing code does "return vrrp_in_chk_ipsecah(vrrp, buffer);" if the VRRP version is two, and the authentication type is IP sec authentication, to check whether or not the IP sec authentication header is valid. However the "instant" returns means that is the IP sec authentication header is valid, then the remaining parts of the VRRP packet (VRRP version, VRRP checksum, VRID, number of VIPs, advertise-interval) are not parsed or validated. * vrrp: Add support for SNMP trap: vrrpTrapNewMaster. * vrrp: Add skeleton code for VRRP-MIB. * vrrp: Check existing VIF and recreate if VMACs are wrong. Although under normal circumstances we will cleanup VIF interfaces when shutdown, there are various scenarios were this is not the case. To make the code more robust, keepalived now performs a check for matching VIF interfaces at restart, and if the configuration of the VIF matches the current keepalived configuration it will reuse the VIF. However, should the configuration be different, keepalived will remove the existing interface, and then recreate a new VIF interface with the appropriate configuration. This fix resolves the continuous crash scenario that can occur when keepalived fails to configure the VIF because one already exists. It prevents keepalived from reusing a previous VIF interface which does not completely match it configuration criteria.` * vrrp: fix snmp code (cosmetic) * vrrp: Fix the keepalived mib and agentx warnings. During Keepalived startup, about twenty "duplicate registration" and a couple of "Failed to connect to the agentx master agent" warning messages were issued. Pairs of the "Failed to connect" warning messages were logged every two minutes. The "duplicate registration" warnings happened because VRRP called snmp_agent_init twice, once for the keepalived-vrrp MIB, and once for the rfc2787-vrrp MIB, however each call to snmp_agent_init also tried to register the keepalived-global MIB (which holds data like Keepalived version number, SMTP server details, and a "from" email address). It was the second attempt to register this keepalived-global MIB that generated the "duplicate registration" warning. The registration of the keepalived-global MIB is now only done once under the control of a static variable. init_agent is also called just once under the control of the same static variable to prevent it logging a warning message. The "Failed to connect" warnings occur because Keepalived does not know how to connect to the SNMP AgentX master server. By default the Agent X master server is listening for MIB registrations on a local TCP socket with a port number of 705. * vrrp: Fix VRRP preemption taking too long. VRRP preemption may not work correctly due to group expiry timers being incorrectly manipulated while running down the MDT. Also, preemption can be disrupted if the VRRP group receives an advertisement while running down it's timer. * vrrp: Initial Implementation of VRRP statistics. . Add VRRP counters, This is needed by the VRRP-MIB, and will provide better insight into the operation of VRRP for users. . Add SIGUSR1 and SIGUSR2 handlers - SIGUSR1 allows users to dump current state of VRRP instacnes to /tmp/keepalived.data - SIGUSR2 allows users to dump VRRP counters to /tmp/keepalived.stats * vrrp: Copy old VRRP stats on reload. * vrrp: Seperate printing functions from vrrp_daemon.c. Seperate state printing code from vrrp_daemon.c so that the code is better organized. * vrrp: Track master router priority in VRRP. * vrrp: Added 'Master priority' output to show vrrp detail. * vrrp: Enhance keepalived vrrp to configure mltp-scripts. Currently, keepalived vrrp only allows to configure single notification script. This is a limitation ans should be extended so that keepalived vrrp can notify multiple scripts about vrrp state changes. * vrrp: Don't display ipsec ah password in log files. When authentication type is selected as ipsec ah, password should not be displayed in the log files. * vrrp: Fix notify upon reload. When a notify script is configured after Keepalived has been started, if other notify scripts are already configured, these scripts get reinvoked even if the state has not changed. This occurs when in backup state. When in master state, no notifications are sent out at all if a new notify script is configured. For the backup case, this problem occurs when the daemon is reloaded. This causes vrrp to leave the state it's currently in, go to the init state and from there, go back to backup. However, this transition causes the notify scripts to be invoked, causing a redundant notification to be sent. For the master case, there is no call to notify_instance_exec(), hence why no notifications are seen at all. The solution is to add a new field to the vrrp struct that stores the notify scripts that were configured before reload. A new function has been added to take advantage of this new field. Instead of calling notify_instance_exec() when we are in the init state, we now call notify_instance_exec_init(). This is a proxy function that modifies the 'script' member of a vrrp structure to point to a new list containing only scripts that have not previously been configured, thereby preventing the sending of notifications that have already been sent. This new list is created by utilising the new vrrp struct field. Inside this new function, notify_instance_exec() is called using the modified VRRP instance. When this call returns, the member is reset back to its original value. * vrrp: Keepalived extension to support VRRP version 3. Updated vrrp_header and _vrrp_t struct to support version 3 params. Support to build vrrp_v3 packet. * vrrp: Keepalived extension to support VRRP version 3 (2). * vrrp: Keepalived extension to support VRRP version 3 (3). Timer changes to support centi-sec. * vrrp: Keepalived extension to support accept mode for v3. * vrrp: Fix up limitations of keepalived VRRPv3. The current Keepalived is supporting IPv6 but it is not fully functional and it is not as per RFC5798. Following are the issues identified and changes done: - IPv6 address population. - Correction of Checksum in case of IPv6. - Getting source address from received advertisements. - Populating source address in sent VRRP advertisements. * vrrp: Improve display output for VRRPv3. - Changed data-type of mcast_saddr to sockaddr_storage to support IPv6 also. - Added new parameters version, accept, weight updated advertisement interval for operational command show output. * vrrp: MIB enhancements for accept-mode. * vrrp: Fix mismatched advertisement interval. In VRRP version 3, all BACKUP routers must set their advertisement intervals to match the current MASTER's. Although not explicitly stated in RCF5798, when the MASTER falls over or forfeits its MASTER status, the new MASTER should not continue to use the old MASTER's advertisement interval value and should instead use its locally configured value. To achieve this, a new field has been added to the VRRP structure that stores the most recent advertisement interval of the current MASTER. We track changes to the current MASTER's interval and update this new variable accordingly. The value is only updated when we are in BACKUP state and reconfiguring the local advertisement interval has no effect on it. * vrrp: snmp: don't hardcode AgentX socket location. The default location should be `/var/agentx/master` (as per RFC2741 and this is also the default for NetSNMP, including on Debian-based distributions). This default location is set at configure-time for NetSNMP and subagent will use it automatically (it is also available through `net-snmp-config.h`). A useful feature would be to have a flag to change that if the user change this settings in the master agent. This commit just reverts this change to let SNMP subsystem work as expected for most users. * vrrp: snmp: restore use of net-snmp-config to build SNMP support. With a lazy linker, `libnetsnmpmibs` may require some additional libraries to be linked (like `libsensors`). Therefore, only rely on `net-snmp-config` to get the appropriate flags. Also add some additional tests: - check that we can build a simple executable (NetSNMP can be quite broken and in this case, the error during compilation is not crystal clear, checking that in configure is more informative) - check if we subagent support is compiled in (This is optional and again, the error is not crystal clear during compilation). - check that net-snmp/agent/util_funcs.h is present (Due to a flaw in NetSNMP build process, this header was not installed for quite a long time, notably on RHEL derivatives; code to handle its absence was already present in Keepalived). * vrrp: snmp: don't enable SNMP support automatically. Most users won't use it and it would fail if NetSNMP is not installed, unless a user add `--disable-snmp` to configure command line. * build: move custom include directives (`-I`) first. Some libraries, notably NetSNMP, may pollute CFLAGS by adding stuff like `-I/usr/lib/x86_64-linux-gnu/perl/5.20/CORE` in CFLAGS. Instead of trying to not use CFLAGS from NetSNMP at all (some of those bits are important as they influence some NetSNMP headers), we ensure that the bogus include flags are after our own include flags. * global: Set global data default values after parsing config file. This patch will defer setting the global data default values until after the config file has been parsed. This will potentially avoid two calls to getaddrinfo. For example, if the router_id and/or email_from parameters are set in the config file, there is no need to call getaddrinfo twice in order to set a default value. Instead, this patch will check to see if they values are unset after parsing the config file. Note that email_from and smtp_connection_to are only set to a default value if they are unitialized and smtp_server is specified. * doc: add -x/--snmp flag to keepalived manual page. * snmp: add -A/--snmp-agent-socket to specify AgentX socket. 2015-03-31 Alexandre Cassen * keepalived-1.2.16 released. * Properly close netlink channel to avoid fd leak. * Use getaddrinfo instead of gethostbyname to workaround glibc gethostbyname function buffer overflow. * ipvs: log http timeout only when server goes down All other calls to log_message() when a check fails are performed when a server changes its state. The http timeout log message is the only exception. * ipvs: properly fix bug when Q < H. The commit a77c2c7 has not fixed the issue. Log messages became accurate, but unsigned comparison was still in use. * ipvs: HUP processing refactored. copy_srv_states is removed: we can copy states with existing clear_diff_*functions, as long as clear_diff_services is called before the init_services. vs_exist, rs_exist: remove side-effects from these functions. Now they do only search and return pointers. get_rs_list removed: the new rs list is now passed to clear_diff_rs. init_service_vs: quorum_state assignment is not needed here. It is already assigned either by vs constructor, or by alpha handler, or by clear_diff_services. * ipvs: refactoring link vsg structure to vs. this adds a pointer to virtual_server_group_t into the virtual_server_t structure and fills these pointers after config load. This change will allow to access vsg items of a vs easily, without iterating and name compare. * ipvs: refactoring use links to vs->vsg links. ipvs_cmd: removed vs_group list parameter. Link to vsg is obtained via vs->vsg. These functions are also modified in the same way: ipvs_group_cmd, clear_service_rs, clear_service_vs, clear_diff_rs. clear_diff_vsg: new_vs is passed as a param, vsg pointers are retrieved w/o iterating. * ipvs: fix problems with config reload. The commit 7bf6fc contained a bad trying to fix the issue when an alive RS does not appear in a new VSG entry on reload. It has not fixed the original issue and added a new one: vs_groups lose quorum on config reload. This commit fixes the issue properly, and also the case when RS in VSG is in inhibit mode. The reloaded flag is added to the virtual_server_group_entry_t. ipvs_group_sync_entry: add alive destinations to the newly created vsge. It is aware of inhibit-on-failure destinations. sync_service_vsg: calls the former for each created vsg entry vsge_exist: changed just as other *_exist routines. * genhash: add support of fwmark in genhash * genhash: terminate thread if connect_error * Fixed filenames and paths so that make uninstall removes initscript and man pages. Changed perms for keepalived.sysconfig from 755 to 644 * Fix a typo in dump_global-data(). * vrrp: revert previous buggy preempt extension. * smtp: fix infinite loop when the smtp server unexpectedly closes the connection. 2014-12-21 Alexandre Cassen * keepalived-1.2.15 released. * vrrp: Use ancillary data on sending path for IPv6 mcast_src_ip. Well, previous code used bind() to specify IPv6 src address. Ancillary data is a much more cleaner and efficient way... * ipvs: Fix format of long int in log_message call. * ipvs: fix building with fwmark disabled. * vrrp: Pointer dereference before NULL check. * STR(SMTP_PORT) returns "SMTP_PORT", not "25". 2014-12-16 Alexandre Cassen * keepalived-1.2.14 released. * The "Date:" mail header is now localtime. * bugfix: fwmark field was formatted as signed int * dump_conn_opts: fwmark was not displayed. * log_message: emit -Wformat= compiler warnings. There could be (and actually are) situations when the format string and the arguments list passed to the log_message() are inconsistent or mistyped. The compiler did not show any warnings because the vsnprintf was called indirectly. * Further unification of IP endpoints logging. This change tries to keep usage of the standard "[%s]:%d" format string to a minimum. Instead, use inet_sockaddrtopair wherever possible. * Add SNMP subsystem option to man page. The keepalived(8) man page did not mention the -x option to enable the SNMP subsystem. This patch adds the -x (and --smmp) options to the keepalived(8) man page, as described in the keepalived help message. * vrrp: fix gratuitous ARP refresh timer handling. Previous code was using an 'int' to store parsed timer value. This value was then expanded to TIMER_HZ which can lead to a wrapping issue if requested timer is longer than local machine 'int' representation. This patch reworked the code to use timeval_t instead and perfrom regular timeval operations. * vrrp: Fix a memory leak while dropping incoming IPSEC-AH authenticated advert. Digest was allocated in previous code without freeing it on HMAC-MD5 missmatch. * vrrp: Extend IPSEC-AH auth to support unicast. If you plane to use IPSEC-AH auth in unicast mode (which THE best idea), then IP header TTL MUST be zeroed since it is mutable field on transit. * vrrp: Update VRRP VMAC doc. Add vmac_xmit_base in configuration example and force rp_filter=0 on macvlan interface. * vrrp: make gratuitous ARP repeat count configurable. . garp_master_repeat : Gratuitous ARP count sent on the wire after MASTER state transition. . garp_master_refresh_repeat : Gratuitous ARP count sent on the wire when garp_refresh_timer fir * vrrp: fix preempt and state BACKUP when prio 255. This makes it so that keepalived will respect various settings that should prevent it from assuming the MASTER role for a vrrp_instance unconditionally and immediately, even if the priority of the vrrp_instance in question is set to 255 (VRRP_PRIO_OWNER). These settings include: ---- conf ---- state BACKUP preempt_delay nopreempt * vrrp: in backup state notify when vrrp is not up and move to FAULT state. * ipvs: failed RS was flapping on config reload. The RS disabled by health-checker was turned on w/o health-checking by SIGHUP handler in the init_service_rs() subroutine. This did not happen with alpha mode set. * libipvs: allow IPv4 RS in IPv6 VS and vice versa. This change syncronizes local copy of libipvs with the upstream (kernel/ipvsadm/ipvsadm.git) to the date. IPVS in Linux 3.18 will include the feature of mixing of tunneled RS families in single VS. The compatibility with older kernel versions is kept. * libipvs: minor bugfix with retreiving dest af. This change needs to be sent to the ipvsadm upstream, too. This clarifies the previous commit, so there is no need to mention it in the changelog. * vrrp: check if interface is known when using use_vmac. vrrp->ifp is NULL when use_vmac keyword is defined before the interface keyword. This would result in a segfault * vrrp: simplify macvlan creation. Create the macvlan interface in one netlink command rather than three (creation of the macvlan in netlink_link_add_vmac function, set of the mac address in the netlink_link_setlladdr function, set macvlan mode in the netlink_link_setmode function). This simplification: 1. avoids potential issues if the firt netlink command passes butcw not the next ones 2. reduces number of netlink messages (light optimization) * ipvs: bugfix quorum state was flapping when Q < H. When a service had quorum < hysteresis, the lower threshold of RS weights was calculated incorrecly. Unsigned arythmetics was used, so errors like this appeared in log: Keepalived_healthcheckers[2535]: Lost quorum 1-2=18446744073709551615 > 10 for VS The up -> down quorum state transition was happening every time when alive RS set was changed. This bug was in place since keepalived-1.2.9 * vrrp: add support to IPv6 mcast src address specification. For some reason (well... which one ?), previous code didnt support specification of multicast source address in IPv6 mode. If you are using 'native_ipv6' and want to specify IPv6 mcast source ip address then you can use 'mcast_src_ip' keyword with IPv6 address. * vrrp: Add support to IPv6 src_address discrimination in master rx state. Previous code didnt support IPv6 address discrimination while in MASTER state receiving same prio advert. This patch extend previous code to support IP address comparison agnostic. * vrrp: IPv6 mcast src_addr handling and VMAC fix. Properly bind socket for v6 use-case when mcasr_src_ip is in use or when VMAC is used. This patch fix VRRP VMAC in native_ipv6 mode, previous code just use the vmac interface link-local IP Address as src_ip leading to a corner case (to keep polite). * vrrp: in IPv6 scope_id is mandatory to bind link-local address. In IPv6 use-case, source IP address is set binding sokect to socaddr_in6. Linux Kernel requires interface to bind link-local address. * vrrp: fix nopreempt mode in master_rx. While receiving lower prio advert, preempt election according to nopreempt keyword. By default preempt is on as requested by RFC. * exit on malloc failure. * genhash: code cleanup. 2014-05-13 Alexandre Cassen * keepalived-1.2.13 released. * vrrp : Use the standard unsigned int types. This fixes building with musl libc, which does not expose the internal __uint* defines. (Natanael Copa) * check : Fix template issue in IPv6 host header. (Jan Hugo Prins) * ipvs : ipvs_syncd_cmd uses memset() to zero the daemonrule buffer before populating it and sending it up. daemonrule is malloc()ed by ipvs_start(). ipvs_start() can bail early if it can't communicate with ipvs. Neither place which call ipvs_start() check the return value, allowing them to walk straight into a NULL pointer deref. (jsgh) * check : Without inhibit_on_failure on a real_server, when the server is marked down existing TCP connections to it are simply blackholed. Hence inhibit_on_failure: by setting the weight to zero no new connections are sent to that server, but because the server isn't completely removed from the table existing connections are allowed to continue. The same problem exists with sorry_server. When a real_server comes back up the sorry_server is removed from the pool and existing connections are blackholed. Instead of continued service, which may usually be a fast response indicating overload, the client must engage in a lengthy wait for the connection to time out. It would be better in many cases to allow the sorry_server connections to complete naturally. Luckily the code is structured well enough that all is required to get this behaviour is to set the inhibit member of the sorry_server structure, which is mostly just a change to the config file parser. (jsgh) * check : unify logging of RS and VS. This fixes the bug of displaying a FWM service as [x.x.x.x]:0, where x.x.x.x is the first RS of that service. (Alexey Andriyanov) * check : unify connection options among checkers. All the remote checkers (TCP, HTTP/SSL, SMTP) now have the same set of connection options: . connect_ip (new to TCP, HTTP) . connect_port . bindto . bind_port (new) . connect_timeout (new to SMTP) All of them are optional with reasonable defaults. The patch is designed for simplicity in adding a new option. Since the connect_ip could be inequal to the RS address and, worse, the same for all RSes, the endpoint is now logged as [RS]:rport, not the [connect_ip]:connect_port. (Alexey Andriyanov) * check : fwmark connection option. (Alexey Andriyanov) * check : make SO_MARK a compile-time option. (Alexey Andriyanov) * check : documentation for generic connection opts. (Alexey Andriyanov) * check : random delay before doing the first check. every RS check is registered with a random delay between 0 and vs->delay_loop seconds. It helps avoiding multiple simultaneous checks to the same RS server. (Alexey Andriyanov) * vrrp : Fix sync of interface status flag when using VMAC interface. There is a chance that the VMAC interface status flags (up/down) could be different from the base interface flags. This patch will only change the VMAC interface status flags when the base interface is changed. (Jonas Johansson) * vrrp : Let only base interface change the VMAC interface status flags. The interface status flags for a VMAC interface shall only be changed by the base interface, never by reading the actual VMAC interface flags. (Jonas Johansson) * vrrp : Fix initial interface status flag value for VMAC interface. In commit a05a503, "vrrp: Fix sync of interface status flag when using VMAC interface", no inital value for the VMAC interface status flag was set. Due to that the VMAC interface flags shall follow the base interface, the base interface status flags value shall be copied to the VMAC interface status flags after the VMAC interface has been created. (Jonas Johansson) * vrrp : Proper restore of VMAC interface properties on SIGHUP. On SIGHUP the VMAC flag and base ifindex for a VMAC interface was lost. (Jonas Johansson) * vrrp : Revert "Honor preempt_delay setting on startup.". This commit resulted in two individual bugs: 1) A keepalived instance coming on-line would not transition to MASTER state until the preempt_delay duration had passed, even though there was no already existing VRRP speaker in MASTER state on the link. In other words, it changed the semantics of preempt_delay from a delay that only took place before *preemption* of another VRRP speaker, to a delay that unconditionally took place after Keepalived came online. The keepalived.conf manual page has always documented the former meaning, which is also IMHO the only one that you would intuitively expect. 2) The preempt_delay was applied when a Keepalived process was reloading its configuration following the recipt of SIGHUP. If the Keepalived instance was in MASTER state before the reload, it would cease transmitting VRRP hellos for the duration of preempt_delay, but *not* actually remove the virtual addresses from the network interfaces. This in turn resulted in any backup VRRP speakers on the links transition to the MASTER state while preempt_delay was still in effect on the original MASTER that was reloaded, thus creating a service-impacting split-brain scenario where the virtual addresses are present and active on multiple VRRP speakers simultaneously. (Tore Anderson) * vrrp : fix ip_address comparison. Extend IP_ISEQ() macro to take care of NULL addresses. This issue end on SEGV while using virtual_route. thanks to Tore Anderson for reporting. * vrrp : fix double close issue (DROP_MEMBERSHIP & netlink channel). This is a old pending 'bug', not arming at all but just frustrating to see again and again this log message : "cant do IP_DROP_MEMBERSHIP errno=Bad file descriptor (9)" What the hell ! it was due to a double close during reload & stop procedure. VRRP fd are stored in a socket pool and use the I/O MUX to handle VRRP traffic. While reloading or stopping the daemon the I/O MUX was released first and secondly socket pool. The issue spotted here, in thread_destroy_master() all pending thread are canceled and read/write fds related are close(). Well OK a close on a mcast socket perform kernel side the DROP_MEMBERSHIP when needed, but it is much more clean to perform proper operations userspace ! This patch sequencely cancel pending thread, release socket pool and finally destroy master thread. Same 'issue' appear in netlink channel. 2014-02-08 Alexandre Cassen * keepalived-1.2.12 released. * lib: Fix reallocation issue introduced in last merge. 2014-01-28 Alexandre Cassen * keepalived-1.2.11 released. * ipvs: make nlerr2syserr libnl dependent. nlerr2syserr() is only used when libnl is present... simply reflect this in libipvs. * Fix libnl/libnl-3 logic in configure script. This patch causes the configure script to prefer libnl-3 over libnl(1). The configure script will first check for libnl-3 and libnl-genl-3. If both are found, use them. If not, check for libnl(1). This is useful when building on systems that have both libnl-3 and libnl(1) installed. It also fixes some redundant libraries in LIBS. * libipvs: libnl-3 include fix. * lib: extend command lib string parser. Extend cmd_make_strvec to support quoted string as a single slot and commented string at the end of parsed string. * lib: cosmetics at command.c. Extend command framework to support logger and remove some dead code. some cosmetics too. * lib: extend vty to support logger. * autoconf: better libnl3 detection. * Fix memory allocation in parser. The set_value function was incorrectly using sizeof (char *) when allocation and reallocating memory. * Fix memory allocation for MD5 digest. The vrrp_in_chk_ipsecah and vrrp_build_ipsecah functions were incorrectly using sizeof (unsigned char *) when allocating memory for the MD5 digest. * Fix memory leak in vty_read_config. If vty_use_backup_config returns NULL, free any memory that has been allocated before returning. * Fix memory leak in check_include. The check_include function should always free the allocated strvec. * Check content length before allocating memory. Since extract_content_length should return 0 if CONTENT_LENGTH is not found in the buffer, this check should be done before allocating memory. This avoids unnecessary malloc/free calls and fixes a potential memory leak. * Free memory if realloc fails in vty_out. If realloc returns NULL, free the original memory before returning. * Remove redundant close from vty_use_backup_config. The sav file descriptor is closed after read, so there is no need to close it again is chmod operation fails. * Remove unnecessary netlink rtattr structures. Both netlink_link_setmode and netlink_link_add_vmac have rtattr structures that are no needed. The addattr_l function will handle adding the rtattr to the message. Also, this patch removes incorrect void pointer arithmetic when setting rta_len. * vrrp: dont try to leave mcast group in unicast mode. * vrrp: Release and refresh properly fd hash index. Rehashing into the same loop as releasing is not really the best idea... Reworked a little previous patch to properly release hash entries related to the same instance and then hash it back on new fd. * vrrp: use configuration mcast group for leave message. * vrrp: dont try to load ip_vs module when not needed. 2014-01-02 Alexandre Cassen * keepalived-1.2.10 released. * Jonas Johansson removed unused option character in getopt optstring. * vrrp: disable TTL sanity check for unicast use-case. In order to protect against any packet injection, VRRP provides sanity check over IP header TTL. This TTL MUST be equal to 255 and means both sender and receiver are attached on the same ethernet segment. Now with unicast extension this protection MUST be disabled since VRRP adverts will mostly traverse different network segments. !!! WARNING !!! When using VRRP in unicast use-case in order to protect against any packet injection the best practice is to use IPSEC-AH auth method otherwise you are exposed to potential attackers ! * Christian Albrecht fixed minor typo in man page * Pim van den Berg work on libipvs-2.6 to sync with libipvs from ipvsadm 1.27 * Pim van den Berg work add support to libnk >= 3. This address following considerations : http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688164 http://article.gmane.org/gmane.linux.keepalived.devel/3522 * Pim van den Berg extended libipvs adding nlerr2syserr function to translate libnl 3 errors to sys errors. In libnl 3 the return codes have changed. nlerr2syserr translates the libnl 3 errors to sys errors. * ipvs: if libnl-3 is installed then check for libnl-gen-3. It is mandatory to use generic netlink facilities in new libipvs. This test is just here to ensure every needed libs are installed ! * Frank Baalbergen (I suppose github frankbb is you ?) fix http checker. literal ipv6 addresses should be enclosed by brackets. * vrrp: Frank Baalbergen add check on IFA_F_NODAD support. * vrrp: fix unicast handling address selection. SjonHortensius reported issue while testing unicast_peer. It wouldn't work without adding the native_ipv6 flag. Removed this dependency ! since it not correlated with VRRP protocol version used. * vrrp: extend ip parser to support default and default6. When you are using virtual_routes you may want to use default or default6 while configuring routes. Extended parser accordingly ! * vrrp: take care of label while comparing IP addresses. Label was not taken into account while comparing 2 IP addresses, this can lead to a non deletion while stopping daemon and some configuration changes have been done while deamon running. This issue was reported by Stepan Rogov. * vrrp: fix/extend gratuitous ARP handling. multiple people reported issues where MASTER didnt recover properly after outage due to no gratuitous ARP sent. VRRP is a protocol designed to be used between node plugged on the same layer2 in order to guarantee link failure is directly linked to a protocol FSM handling (FAULT transition). With current virtualization env quite every think can be virtualized from host (VM) to network (vswitch). In some cases those virtualized env offer a virtualized layer2 on which VRRP is plugged and sometime forwarding or routing over this virtual path can be broken. I extended gratuitous ARP handling in 2 ways : 1) When a MASTER receive a higher prio advert it sends a last advert before transiting to BACKUP state. The immediate effect at remote MASTER side is to sollicite a gratuitous ARP broadcast. 2) Add an optional support to periodic gratuitous ARP sending while in MASTER state. By default it is disabled but one can activate this feature by configuring keyword "garp_master_refresh" in seconds in vrrp_instance block (refer to keepalived.conf.SYNOPSIS). * Frank Baalbergen fixed genhash. genhash can throw a segmentation fault when not providing an argument * Frank Baalbergen extended genhash code to support IPv6 * Frank Baalbergen extended genhash code to make url default value /, same as curl/wget * Frank Baalbergen extended genhash code to only use default url when url is empty * vrrp: Create configuration alias for unicast_src_ip keyword. Add a new keyword more generic to specify VRRP packet source IP address. This new keyword is "unicast_src_ip" and have exactly the same scope as "mcast_src_ip". * vrrp: unicast_peer addresses and VRRP instance MUST be of the same family. VRRP low-level framework create socket pool based on VRRP instance family. If you are using unicast_peer, it is mandatory to use addresses of same family as VRRP instance. You cant mix IPv4 and IPv6 addresses inside same unicast_peer block. If you need to make it that way, you MUST create a VRRP instance per family, eg: one with native_ipv6 for v6 unicast_peer and another for v4 unicast_peer. * vrrp: extended unicast code to support IPv6 unicast_src_ip. Add support to unicast IPv6 address for {unicast,mcast}_src_ip keyword. vrrp instance saddr is now a sockaddr_storage and src IPv6 address is set using cmsg ancillary data pktinfo. TSource IP address selection is now generic and can be IPv4 or IPv6. * vrrp: fix vrrp socket sync while leaving FAULT state. Well, this is a very, VERY old bug here. while leaving FAULT state VRRP framework refresh instance socket fd_in & fd_out and synchronize all VRRP instance bound to the same socket. The patch refresh socket, it also refresh fd hashing ! which better for later fault handling :) * vrrp: Frank Baalbergen fix log-facility handling. log-facility should be a required_argument * vrrp: Support xmit VRRP packets from base VMAC interface. Here is a merge of patch from Oliver Smith. Thanks for your job and idea in here Oliver. Comments from Olivier : This provides a new option to use in conjunction with the VMAC functionality which will result in VRRP advertisements being sent and received over the underlying interface (and therefore having the source MAC of that interface rather than the VMAC device). With this new functionality enabled, VRRP messages will not affect the switch MAC address table since the non-unique VMAC address is now used only for sending a gratuitous ARP, thereby ensuring that in conditions of VRRP message loss, a probing partner will not inadvertently take over traffic. This also resolves issues where VRRP messages are not successfully being seen on the VMAC interface as with the new option, the underlying interface is also used to listen out for VRRP messages. * getopt: Make some arguments required * vrrp: Frank Baalbergen add default case in getopt_long. when starting keepalived with an option without an argument that requires an argument keepalived should not be started. * vrrp: VMAC code cleanup and extensions. Remastered VMAC code. Interface base_ifindex is set by default to interface ifindex during netlink probe. VMAC interface base_ifindex is now set during VMAC allocation uppon success interface creation. Detect if virtual_router_id is declared after use_mvac keyword is invoked. Add some more log while setting up and removing VMAC interface. * vrrp: IPv4 & IPv6 multicast group tweaking. Meno Abels extended vrrp framework to support customized multicast addresses. The address could be set for ipv4 and ipv6 in the global_defs config section using the keywords vrrp_mcast_group4 and vrrp_mcast_group6. There are some stupid switches which does a special processing to 224.0.0.0/8 multicast packets which causes packets drop from queue overflows in environments which creates 100 and more multicast control plane packets a second. 2013-11-10 Alexandre Cassen * keepalived-1.2.9 released. * Alexey Andriyanov fixed inaccuracy in VS_ISEQ macro. * Alexey Andriyanov fixed hysteresis which could be >= quorum now. * Alexey Andriyanov extended checker framework so that status_code and digest can be set together. * Alexey Andriyanov extended/fixed checker framework for better SIGHUP support. * Jonas Johansson fixed VRRP sync group by sending prio 0 when entering FAULT state. This fix will send prio 0 (VRRP_PRIO_STOP) when the VRRP router transists from MASTER to FAULT state. This will make a sync group leave the MASTER state more quickly by notifying the backup router(s) instead of having them to wait for time out. * Jonas Johansson extended VRRP VMAC interface flags (up/down status) to follow base interface. When using a VMAC interface, this fix will reflect the base interface flags, i.e. up/down status, to the VMAC interface. This is useful when using sync groups (in combination with VMAC) and a link for one of the members in the MASTER sync group goes down. Before this fix, this member will not detect the link fault, due to that the VMAC interface always is UP regardless of the actual status of the base interface, and the sync group will continue to be MASTER as if nothing has happend. This fix will however reflect the status of the base interface onto the VMAC interface, so if the link goes down the member will transit to FAULT state, which will make the sync group transit to BACKUP state. * Jonas Johansson fixed VRRP wrong interface flags corner case. If a link event arrives between the initial scanning for interfaces and configuration file parsing, the VRRP instance will enter an unrecoverable state. This fix will update the interface flags even when the interface exists, not only for the inital scan. Note that when all is up and running the link events will be properly handled by netlink, so this fix only fixes the special case when a link changes state during initalization/configuration. * Jonas Johansson fixed VRRP to honor preempt_delay setting on startup. If the preempt_delay is set we cannot yet transition to master state. We must await the timeout of our preempt_delay. The preemption delay is used when starting up, or rebooting, a node which needs time to sort out its routing table (e.g., BGP or OSPF) before it can assume the master role. * Jonas Johansson extended VRRP code for faster sync group transition. * Jonas Johansson replaced popt with getopt. In a embedded environment you might not want to have to add yet another library dependency. This commit refactors parse_cmdline() to use getopt_long() instead och popt. * EyckWigo proposed to increase defaut socket buf size to handle env with lot of IP addresses, Default is now set to 64K on netlink socket. * Guðmundur Bjarni Ólafsson fixed VRRP unicast code to allow packet to be routed ! * Guðmundur Bjarni Ólafsson fixed VRRP checksum before computation. When running in unicast mode with multiple peers, the checksum was being calculated into itself for consequent peers, causing incorrect checksums. * Extended VRRP framework tweaking IPv6 VIP install by disabling DAD algo and setting deprecated. Lot of discussions have been made around those 2 topics. First idea and initial patch where provided by Leo Baltus. This patch fix the use case where VRRP VIPv6 are used in conjonction of IPVS healthchecking. If deprecated flag is not set (which is the default linux behaviour), then VRRP VIP can be used as source address of healthcheking packet. Since this VIP address is also present, in most use-cases, on realserver directly so return packets never reach the healthchecker and hence no realserver was injected in IPVS table. At the same time, I decided to merge Tore Anderson suggestion of disabling Duplicate Address Detection algorithm. Tore's arguments are nice ! Thanks Tore : Using the nodad flag has the following benefits: 1) The address becomes immediately usable after they're configured. 2) In the case of a temporary layer-2 / split-brain problem we can avoid that the active VIP transitions into the dadfailed phase and stays there forever - leaving us without service. HA/VRRP setups have their own "DAD"-like functionality, so it's not really needed from the IPv6 stack. Acknowledgements to Mark Schouten and Frank Baalbergen for pushing me by testing this features ! 2013-09-05 Alexandre Cassen * keepalived-1.2.8 released. * Vincent Bernat fixed issue while pinging master agent. The agent needs to be initialized to be able to change the AgentX ping interval. * Revisited the whole code to use posix declaration style. * fixed some typos * Created CLI core framework. * Ryan O'Hara added option to prevent respawn of child process. This patch adds a command-line option (--dont-respawn, -R) that will prevent the child processes from respawning. When this option is specified, if either the checker or vrrp child processes exit the parent process will raise the SIGTERM signal and exit. * Ryan O'Hara removed duplicate command-line option code. patch removes unnecessary code to process command-line options. All options can be processed with a single while loop that calls poptGetNextOpt. This patch also adds code to check for errors while processing options. Note that errors encountered while processing command-line options are fatal. * Ryan O'Hara add support to usage generation by popt. This patch uses the popt library to describe the command-line options and print usage to stderr. This provides a more clear, concise usage statement. * Ryan O'Hara and I updated keepalived man page. * Aleksei Ilin add flexible HTTP checker behaviour for HTTP GET request's port settings. VirtualServer's port being specified in HTTP GET request only if `VirtualHost` option is not defined, otherwise used `VirtualHost` option itself. * Ryan O'Hara fixed pointer arithmetic for VRRP packet. When using IPSEC AH authentication, the pointer arithmetic used to get the location of the VRRP packet is incorrect. The address of the IPSEC header must be cast as (char *) in order to get correct address of the VRRP packet. Without this patch, vrrp_in_chk() will fail to verify incoming VRRP packets when IPSEC AH is enabled. * Ryan O'Hara fixed issue while loading SSL certificate. This patch fixes a problem where keepalived will attempt to load an SSL keyfile as a certificate, resulting in failure to initialize SSL context. * Ryan O'Hara refreshed GPLv2 license with last FSF file. * junpei-yoshino fixed configure.in. Library crypt is needed. * Boon Ang fixed comparison of primary IP addresses. If a router in the master state receives an advertisement with priority equal to the local priority, it must also compare the primary IP addresses (RFC 3768, section 6.4.3). The code to handle this was comparing two IP addresses with different byte-ordering, resulting in multiple routers in the master state. This patches resolves the problem by coverting the local primary IP address to network byte order for the comparison. * Henrique Mecking fixed memory leak in libipvs * Robert James Hernandez fixed RETVAL by setting RETVAL for status instead keeping RETVAL set to default of 0 * Robert James Hernandez fixed RETVAL by setting RETVAL for catch all and so that it exits like all other matches in the case * Jan Pokorný fixed genhash to ensure CLRF{2} HTML body separator won't slip. * Jan Pokorný extended genhash. Generalize the hash algoi parts, add SHA1. This patch adds support for hash algo suite extension with SHA1 being a first one to be available together with a default MD5. The remaining change on the health-checker subsystem side is to make analogous modifications and to teach it to recognize the intended hash algorithm based on the length of the digest (provided that extra care is taken that no two algorithms will ever alias in this regard). Also the test script for genhash was extended to conditionally use SHA1. * Jan Pokorný cleaned up genhash code. Access to the hash-specific context was simplified as I've now checked some C guarantees regarding union/it's members initial address vs. aligning so now extra inlined accessor function is needed. This simplified the code a bit. Also now the hash-specific object is directly pointed to by SOCK object instead of carrying just the index to the table of hashes and doing the respective access via a global again and again. Next, I've concentrated some hash-related declarations to the new hash.h file. This was mostly motivated by a need to break the circular include dependency that have arisen. As a consequence, part of the recent clutter I brought in was removed again. Most of FEAT_SHA1 conditional compilation is here. Previously separated table in main carrying the hash IDs to be printed in the help screen was merged into the table carrying all the other necessary information about the particular hashes. * vrrp: Remi Gacogne fixed invalid use of sizeof. * Pasi Kärkkäinen Add To header for SMTP alerts. * vrrp: Robert Sander add IPv6 support for virtual_routes and static_routes. * Erik de Groot add support to LVS One-Packet Scheduling (known as OPS). Typically RADIUS traffic comes from a limited amount of clients and thus you have a very limited range of IP tuples in action which will never expire. Issue with Keepalived without this patch is that, although it correctly re-assigns traffic when a real server dies, it will never re-assign traffic back to the real server when it is restored. This is because LVS creates virtual connections, for each IP tuple, that will never time out as the clients keep sending traffic to the server. With this patch is is possible to enable OPS for UDP virtual servers which means LVS does not create virtual connections and takes a new loadbalancing decision for each UDP packet. The result is that a restored server now gets RADIUS traffic as soon as LVS has taken it it back into the server pool. * Willy Tarreau and Ryan O'Hara add the ability to use VRRP over unicast. Unicast IP addresses may be specified for each VRRP instance with the 'unicast_peer' configuration keyword. When a VRRP instance has one or more unicast IP address defined, VRRP advertisements will be sent to each of those addresses. Unicast IP addresses may be either IPv4 or IPv6. If you are planing to use this option, ensure every ip addresses present in unicast_peer configuration block do not belong to the same router/box. Otherwise it will generate duplicate packet at reception point. 2012-08-29 Alexandre Cassen * keepalived-1.2.7 released. * vrrp: fix issue in while using vrrp_script. Previous patch introduced by Ryan O'Hara about removing shadow declaration was kind of too much hunting. Removing element e in this block simply create inconsitency in upper list walk. So resurected element declaration with e2. * snmp: Mikhail Gaydamaka extended MIB and both vrrp and check frameworkds to support routerId to var bind. * snmp: Mikhail Gaydamaka fixed oid for vrrpSyncGroupStateChange var bind. * some cosmetics again and again. 2012-08-20 Alexandre Cassen * keepalived-1.2.6 released. * Rename global config data variable 'global_data'. From Ryan O'Hara : This patch renames the global configuration data variable from 'data' to 'global_data'. Three reasons for renaming this varibale: - Fixes shadow declaration of 'data' in several locations. - Is more consistent with other global data variables (ie. vrrp_data, check_data). - Functions like free_global_data and dump_global_data were ignoring conf_data_t argument and using global variable instead. * Ryan O'Hara: Fix shadow declaration of 'vrrp_data' variable. * Ryan O'Hara: Fix shadow declaration of 'check_data' variable. * Ryan O'Hara: Remove shadow declaration of 'element e' in vrrp_init_state. * check: Avoid the use of kernel defines in libipvs userland prototypes. * vrrp: Correctly handle macvlan interface when config file is re-loaded. From Bob Gilligan : Testing with the 1.2.0 branch, bring keepalived up with a vrrp_instance that is configured with use_vmac. Then delete that vrrp_instance from the config file. Then tell keepalived to re-read its config file with SIGHUP. The vrrp_instance will be stopped, but the macvlan interface will remain. The obvious fix would be to add code to call netlink_link_del_vmac() in clear_diff_vrrp(). There's one problem with that: the code needs the ifindex of the macvlan interface to delete it, but that resides in the interface structure that was freed earlier in the reload process. My fix is to add a field to the vrrp_rt struct to remember the macvlan ifindex. This patch addresses this problem plus two others that can occur in reloading the config file: 1) If the vrrp_instance configuration is kept, but the use_vmac entry is removed, the macvlan interface will not be deleted; 2) If a vrrp_instance with use_vmac is left unchanged, the code will attempt to re-create the macvlan interface, but this will fail and the program will end up not using the macvlan interface. * vrrp: VRRP should notify other routers before it does any action that effects traffic flow. From John Southworth: Move the shutdown_vrrp_instances code to before the deletion of sock_pool. Move sending priority 0 adverts to before address removal occurs * vrrp: From John Southworth: Stop timers before shutting down vrrp instances. This is to avoid a possible condition where a priority 0 advertisement is sent and before the master thread is killed another advertisement can be generated and sent. * vrrp: Change when socket fd's are freed. From John Southworth: Priority 0 advertisements were not being sent as desired on config reload. This was causing long delays on manually failed over instances. The socket pool was being freed too early, as a result the file descriptor for the socket was no longer valid at the time the priority 0 advertisment was attempted. * vrrp: Added a separate timeout parameter for vrrp_script checks From Jonathan Harden: I've added a timeout parameter to the vrrp check scripts which allow you to have the check timeout different to the interval. When no timeout has been specified the interval is used (which mimics the current behaviour). To explain the reasoning: We wanted to have check scripts time out faster than our check interval. Doing the check we need to perform is a little load intensive and so we don't want to perform it every few seconds. With this patch we set an interval of 60 seconds but a timeout of 5 seconds (if the check takes more than a few seconds then the service is not working correctly). * Extended vector lib for futur work * some cosmetics. 2012-08-13 Alexandre Cassen * keepalived-1.2.5 released. * Merge SNMP support from Vincent Bernat. * SNMP is not compiled nor activated by default. * Updated autoconf script * Created Keepalived MIB * Integration of NetSNMP into main scheduling loop * vrrp: Most internal data can be queried with SNMP. * check: Most internal values can be queried using SNMP. The main exception is the ability to query checkers which is not present. * check: SNMP support for IPVS stats. IPVS stats are exported with SNMP. A cache is used to avoid to query the kernel too much. * Created core framework for SNMP trap * vrrp: SNMP traps are sent when instance state changes and when sync group state changes * check: SNMP traprs are sent when real server state changes and when virtual server quorum state changes * vrrp: add support to write/update operations from SNMP. Write/update support is available for changing the base priority and for changing instance preemption. * check: add support to write/update operations from SNMP. Write support is available for changing the weight of a real server. * workaround for AgentX ping blocking Keepalived. When establishing AgentX session with the master agent, we setup low timeout and retries values. If the master agent is blocked, we will wait for less than 1 second for them and therefore, there will be no disruption for VRRP. * Copyright update * some cosmetics. 2012-07-27 Alexandre Cassen * keepalived-1.2.4 released. * Please look at git repo for credits. * remove CR from manpage * check: fix pid display in syslog messages * vrrp: better documentation of the limitation on password length * cosmetics to be pleasant with GCC4 * Update autoconf script to properly detect VRRP VMAC support * security: Fix exploitable issue in sighandler ! * Add datarootdir to Makefile.in files. * Fix logging to console. * Remove newlines from log_message calls. 2012-07-13 Alexandre Cassen * keepalived-1.2.3 released. * Please look at git repo for credits. * VRRP : allow group to use priority with 'global_tracking' group keyword * VRRP : Adjust TOS values. The TOS value used by other vendors is ip precedence 6, so change that. Use socket priority option to force packets into band 0 of pfifo_fast. * VRRP : Fix sync-group thrashing.The sync group implementation was not very robust. If one synced instance lost communication without going to fault state then all synced intances would transition to master. Following this all instances would transition back to backup because they heard higher priority advertisements. This thrashing would continue indefinitely. To fix this the sync-group code was made to prefer backup state. That is, the sync-groups don't sync to master state unless every instance wants to be master. * VRRP : Fix dst lladdr in IPv6 Unsollicited NA. * VRRP : fix pid display in syslog messages. * Fix configure script to correctly identify kernel version. * check : handle unspecified sockaddr_storage when comparing * VRRP : ensure VRRP script interval and GARP delay is not 0. * check: ensure non 0 default values for timeouts. * VRRP : Fix priority not changing on reload. * check : Fix IPv4 address comparison routine. * Don't use bind() with AF_UNSPEC. * check : enable the use of fwmark with IPv6 virtual servers. * Fix modprobe arguments. * Fix double ntohs() in SMTP checker. * Pretty-print IP:port as [%s]:%d. * check : keep retry in case of early TCP failures in checks. * when specifying an IPv6 range, range is hexadecimal value. * Only define kernel types for ip_vs.h header to avoid problems when loading other headers. * When respawning VRRP or check process, use LOG_ALERT. * Do not set reload flag in the main process. * Set correct rights on PID file. * fix 'gratuitous' typos. * ipvs: don't include linux/types.h or asm/types.h. * configure: check for nl_socket_modify_cb for libnl. * configure: don't check for IPVS support with kernel 2.6.x. * VRRP : On shutdown, release sockets later to be able to send shutdown packet. * fix documentation on linkbeat_use_polling keyword. * Fix a typo for healthchecker. * fix syslog message if bogous vrrp packet (wrong auth type) received. * manpage update. 2011-01-09 Alexandre Cassen * keepalived-1.2.2 released. * IPv6 : extended autoconf script to support libnl detection. IPv6 IPVS kernel subsystem is reachable through generic netlink configuration interface. * IPv6 : Enhanced libipvs-2.6 to support generic netlink configuration interface. If generic netlink is available with kernel then it will be the prefered path to configure IPVS. * IPv6 : Enhanced the whole checker API to use sockaddr_storage. * IPv6 : Enhanced the whole core framework to use sockaddr_storage. * IPv6 : Enhanced all checkers to use sockaddr_storage. * fixed a long time pending issue in all checkers. If first connection attempt to remote peer was failing no more check was performed to service... Up on error connecting remote peer simply register a new timer for next check. This is specially needed in IPv6 context where a unreachable host can be reached at delayed time. * code clean-up: revisited the code to use more POSIX compliant declaration. thread typedef to use thread_t instead. revisisted checker framework to use POSIX typdef declaration. 2010-12-08 Alexandre Cassen * keepalived-1.2.1 released. * Vincent Bernat luffy.cx>: VRRP: Fix incorrect computation for packet size * Vincent Bernat luffy.cx>: VRRP: handle passwords up to 8 characters * Vincent Bernat luffy.cx>: When updating weight, check quorum state. MISC check can update the weight of a real server. This can lead to a change in quorum state. We factor out quorum handling from perform_svr_state() into a new function update_quorum_state() that will check if the quorum state changed and if yes, update sorry server status, exec quorum commands and add back or remove alive real servers (with existing function perform_quorum_state()). This patch is mostly cut'n'paste and adding a call to update_quorum_state() in update_svr_wgt(). We also make perform_svr_state() and update_quorum_state() almost symmetric. * Vincent Bernat luffy.cx>: Fix an infinite loop in master transition with sync groups. This patch is from Arjan Filius. See: http://marc.info/?l=keepalived-devel&m=128212278218825&w=2 When transitioning to master state, keepalived might try to force transition to master state of other VRRP instances into the same group before their transition is complete. This leads to an infinite loop with huge VRRP trafic. * Vincent Bernat luffy.cx>: VRRP : Use VRRP_PRIO_DFL instead of 100 for default priority. * Vincent Bernat luffy.cx>: Use netpacket/packet.h instead of linux/if_packet.h to get sockaddr_ll. linux/if_packet.h pulls linux/types.h that should not be used by a userland program since types defined here can conflict with stdint.h. We use netpacket/packet.h which is a GNU LibC header. * Vincent Bernat luffy.cx>: Keep current weight on reload when initial weight is not altered. Weight can be changed by MISC_CHECK when using dynamic option. In case of reload, the change is lost until the script runs again. We record the initial weight in a separate variable and use it to check if a real server has changed instead of using the actual weight. * Vincent Bernat luffy.cx>: VRRP : disabled scripts and initially good scripts should be considered as OK. When a script is not weighted, its failure will lead to a failure of the associated VRRP instance. However, disabled script and scripts that are initially good (after a reload) should be considered as successful and not make the instance fail. Moreover, a disabled script should not be used when computing script weights. * Vincent Bernat luffy.cx>: VRRP : more informative message when disabling a script due to use of weights. When using a weight for a tracked script, the script is disabled. However, the warning message said that the weight was ignored. We change the message to tell that the script is ignored. Moreover, we don't change its weight since it can be used in another instance, not in a SYNC group. * Vincent Bernat luffy.cx>: check : include missing virtual server group name in a log message * Vincent Bernat luffy.cx>: configure: add a check for ETHERTYPE_IPV6. ETHERTYPE_IPV6 defined in net/ethernet.h is pretty recent. If absent, we hard-code the value into CFLAGS. This patch requires regeneration of configure. * Vincent Bernat luffy.cx>: check : update server weight in IPVS only if server is alive and in the pool. With inhibit_on_failure, a server can be in the pool and not alive. We don't want to set the weight of an inhibited server or a server in a virtual server whose qorum is not met yet. * Vincent Bernat luffy.cx>: check: really add back inhibited server when quorum is gained A previous change contained an erroneous check to add back alive servers when quorum state was gained. This check was incompatible with inhibit_on_failure. When servers were added back in the pool, the weight was not updated accordingly. * Vincent Bernat luffy.cx>: check : update server weight despite quorum when no sorry server. In absence of a sorry server, the logic is to not use quorum except to run commands when quorum is gained or lost. This means that if a MISC check modifies the weight of a server and there is no sorry server, we do not consider quorum. 2010-05-31 Alexandre Cassen * Branch 1.2.0 created. This branch will host all new developments on Keepalived. New code will be added in here only. * VRRP : Add support to IPv6 protocol. The global framework has been extended to support this branch new family ! * VRRP : Implement IPv6 Neighbour Discovery protocol (NDISC). In IPv6 gratuitous ARP doesnt exist since ARP is IPv4 only. NDISC can provide the same feature by sending so called Unsolicited Neighbour Advertisement. A node can send such a protocol datagram in order to (unreliable) propagate new information quickly (rfc4861.4.4). NDISC build an ICMPv6 message with taget link-layer address option, this option is set icmp6_override flag to indicate that advertisement should override an existing cache entry and update the cached link-layer. * VRRP : Extend ip address framework to be IPv4 and IPv6 independant. An ip address, as defined in framework, is now {IPv4,broadcast} or {IPv6}. Use struct ifaddrmsg to store and prepare netlink related operation. This clean- -up the code. * VRRP : Extend parser to support IPv6 declarations. IPv6 and IPv4 addresses can be configured inside the same configuration block (eg: virtual_ipaddress or virtual_ipaddress_excluded). An instance can run IPv4 and IPv6 addresses at a time, this can be useful in dual-stack env (since this will become certainly the most common use case in the next years). * VRRP : Extend netlink framwork to support IPv6 addresses interactions (reflection/addition/deletion). * VRRP : Extend finite state machine support IPv4 & IPv6 at a time. * VRRP : Extend protocol helpers to support IPv6 multicast related. AF_INET6 SOCK_RAW tweaking it done through socket API instead of PF_PACKET header building... This makes code cleaner. * VRRP : Set default VRRP instance protocol to be IPv4. you can use configuration keyword "native_ipv6" inside vrrp_instance configuration block to specify that you want to use IPv6 for VRRP multicasting protocol instead. * VRRP : Extend socket option related helpers to support IPv6 specifics. * VRRP : Extend protocol scheduler and dispatcher to support IPv6. * VRRP : Extend socket pool to keep track of socket family. * VRRP : Cleanup protocol offset pointer by removing duplication code... * VRRP : some code clean-up... 2010-05-06 Alexandre Cassen * keepalived-1.1.20 released. * Vincent Bernat luffy.cx> extended ip/route framework to be able to add route or ip address if they already exist. * Vincent Bernat luffy.cx> fixed broadcast address display. * Vincent Bernat luffy.cx> extended genhash to display an error when giving an incorrect IP address. * Vincent Bernat luffy.cx>: When parsing "blackhole" route, also parse IP mask. * Vincent Bernat luffy.cx>: On reload, destroy signal pipes before recreating them. * Vincent Bernat luffy.cx>: Fix SMTP checker adding himself repeatedly in the list of failed checkers. * Vincent Bernat luffy.cx>: Handle non-existant default interface in VIP definition. * Vincent Bernat luffy.cx>: Remove alive real servers when quorum is lost. * Vincent Bernat luffy.cx>: Fix a segfault when a virtual_server is empty. * Vincent Bernat luffy.cx>: Add real servers to new member of a virtual server group on reload. * Vincent Bernat luffy.cx>: Keep previous effective VRRP priority on reload. * Vincent Bernat luffy.cx>: Fix VRRP script not running any more after reload. * Vincent Bernat luffy.cx>: On reload, keep status for all VRRP scripts. * Removed IPVS Kernel 2.2 support 2009-10-01 Alexandre Cassen * keepalived-1.1.19 released. * Cosmetics changes. * Vincent Bernat luffy.cx> fix a segfault when there is no real server for a virtual server. * Vincent Bernat luffy.cx>, Willy Tarreau and I finally fixed SIGCHLD handling upon reload. * Vincent Bernat luffy.cx> fix VS_ISEQ macro. * VRRP : Kimitoshi Takahashi clustcom.com> fixed nopreempt from FAULT state. The owner of higher priority in FAULT state shouldn't preempt current MASTER when it's recovering, if the nopreempt option is set. 2009-09-24 Alexandre Cassen * keepalived-1.1.18 released. * Fixed compilation warnings * Updated autoconf kernel version detection. Created a new configuration option to force kernel versioni selection. This option can be useful for crosscompilation: --with-kernel-version={2.2|2.4|2.6} * Updated media link failure detection strategy. Kernel linkwatch has been around for long time so set it as default strategy. Alternatively you can choose to use MII BSMR polling strategy by adding new keyword 'linkbeat_use_polling' in your configuration file. * Vincent Bernat luffy.cx> fixed ip_vs.h includes. * Removed vrrp_running and check_running test since it is already performed by keepalived_running. * Properly handle father pidfile handling. * fixed reload handler to properly print out PID. * Willy and I fixed a signal handling issue while reloading daemon. A dereferencing master thread issue leading to a segfault, so that reload was seen as a restart because it was respawned by keepalived father process. * Willy fixed a missing UNSET_RELOAD declaration leading to a potential infinite loop while performing reload. * Vincent Bernat luffy.cx> fixed initial value of quorum state on startup and reload. Fixed sorry server removal to consider quorum state. * VRRP : Add missing notify calls while entering FAULT state. * VRRP : Willy added support to delayed script check launch (up and down). It defines "rise" and "fall" keywords. "fall" defines the required number of failures to switch in KO mode, "rise" defines the number of sucesses to switch in OK mode. * VRRP : Fixed an IP_DROP_MEMBERSHIP issue while performing reload. vrrp socket pool is released at first. 2009-03-05 Alexandre Cassen * keepalived-1.1.17 released. * Fixed low-level scheduler timer computation to take care to monothonic computation. Select returns if timer is null! * VRRP : Fixed vrrp script initialization to use event thread instead of timer thread so that script no longer need to wait until first polling timer fired. * VRRP : Willy and I fixed MII media link failure detection to test SIOCGMIIREG call before fetching BMSR. * VRRP : Resurected VRRP_STATE_GOTO_FAULT. This state is really needed to speed-up convergence and prevent against any issue while using vrrp_sync_group. 2009-02-15 Alexandre Cassen * keepalived-1.1.16 released. * Code clean-up. * Stefan Rompf, extended scheduler to synchronize signal handling by sending the signal number through a self pipe, making signals select()able. Child reaping has been moved to a simple signal synchronous signal handler. Signal shutdown handling has been centralized. * Denis Ovsienko, extended healthchecker framework to support alpha/omega design. It provides virtual service control in a more fine-graned maner. You may have a look to the SYNOPSIS file to have full picture on configation. It addresses the following issues : - A virtual service is considered up even with an empty RS pool. - There is no reliable mean to avoid service regression, when the server pool becomes too small. - There is no mean to escalate any of the above fault/recovery events. - Real servers are assumed alive initially. This leads to unnecessary state flap on keepalived start. - notify_down isn't executed for working real servers on keepalived shutdown. - There is no reliable mean to handle keepalived stop to move the virtual service over another load balancer. * Stephan Mayr, fixed default value for checker loop... a missing TIMER_HZ. * Merge keepalived.init.suse. * Robin Garner, added support to --log-console facility. * Tobias Klausmann, fixed an openfile leak while performing reload. * Leo Baltus, extended pidfile handling to allow keepalived to start using configurated pidfile. * VRRP : Siim Poder, fixed IPSEC AH auth to skip IPv4 id field of zero. If zeroed kernel will fill it and lead to an unwanted protocol re-election. * VRRP : Siim Poder, fixed reloading issue. New ip addresses are added (from configuration). State is kept instead of starting from whatever is in configuration file. If prios are changed in such a way, state change can occur after reload. * VRRP : Vincent Bernat, extended virtual_route to support virtual "black hole" route as well as multihop route. * VRRP : Stig Thormodsrud, fixed a crash while using virtual_router_id set to 255. * VRRP: Jon DeVree, fixed arp handling to to initialize the target hardware address, using 0xff as found in arping. Let scripts work without dealing with weight, if the script fails, VRRP fails. * VRRP : Pierre-Yves Ritschard, removed the GOTO_FAULT state from FSM. * VRRP : Willy Tarreau, fixed link detection handling to support right ioctl values for recent kernel ! It can lead to issue while running instance on a bonding interface. * VRRP : Willy Tarreau, extended scheduler to catch time drift. It implements an internal monotonic clock. It maintains an offset between sysclock and monotonic clock, if computed time if anterior to monotonic time then just update offset. If time computed if fare away into the future then limit delay and recompute offset. * VRRP : Willy Tarreau, fixed autoconf issues. 2007-09-15 Alexandre Cassen * keepalived-1.1.15 released. * Matthias Saou, fixed genhash Makefile for man page installation. * Casey Zacek, provided a patch to check_http to remove buffer minimization while processing stream. It appears some webserver cause healthchecker crash. * Chris Marchesi, provided a patch for better handling of SSL handshake errors. * Shinji Tanaka, fixed parser "include" directive to support declaration inside configuration directives, like including file inside vrrp_instance declaration. * Andreas Kotes, fixed HTTP healthchecker while handling MD5SUM result. It appears checker never removed realserver on MD5SUM mismatch !!! whats that crap. * VRRP : Willy Tarreau, fixed a missing notifications upon transition from fault to backup. * VRRP : Add support to route metric in virtual_routes definition. 2007-09-13 Alexandre Cassen * keepalived-1.1.14 released. * Shinji Tanaka, extended parsing framework to support "include" directives. For more informations and documentation please refer to Shinji website : http://misccs.dyndns.org/index.php?keepalived%20include%20patch * Tobias Klausmann, add error loggin while parsing configuration file. * Merged patches from rpmforge.net on Makefile and redhat specfile. * Create a goodies directory to store nice scripts received from users. Add Steve Milton (milton AT isomedia.com) arpreset script to delete a single ARP entry from a CISCO router. * VRRP : David Woodhouse, fixed vrrp_arp includes. * VRRP : Pierre-Yves Ritschard, fixed negative weights in script. * VRRP : Michael Smith, extended virtual_ipaddress setting to support Old-style Linux interface aliases like eth0:1. * VRRP : Ward Wouts, add support to vrrp_script logging. 2006-10-11 Alexandre Cassen * keepalived-1.1.13 released. * VRRP : Added a new notify script to be launch during vrrp instances shutdown. This new notify hook is configured using notify_stop keyword inside vrrp_instance block. * VRRP : Willy Tarreau fixed an errno issue in thread_fetch(), errno is lost during set_time_now(). This patch saves it across the call to set_time_now() in order to get the valid error. * VRRP : Willy Tarreau extended timer framework to save errno in timer_now() and set_time_now() just in case other functions do not expect these functions to modify it. This is a safer approach than the initial patch to thread_fetch(), while still compatible. * VRRP : Willy Tarreau fixed an FSM silent issue. By default, the VRRP daemon stops sending during new MASTER elections. This causes 3 to 4 seconds of silence depending on the local priority, and sometimes causes flapping when the differences in priorities are very low, due to the kernel timer's resolution : sometimes, the old master receives a first advertisement, enters backup, waits 3 seconds, sees nothing and finally becomes master again, which forces a new reelection on the other one. * VRRP : Willy Tarreau extended VRRP framework to support floating priority. Replace the priority in each vrrp_instance with a base priority and an effective priority, to prepare the support for floating priorities. The configuration sets the base_priority, and all comparisons use the new effective_priority value. This one is computed in the vrrp_update_priority() thread by adding an offset to base_priority, based on the result of various checks. * VRRP : Willy Tarreau extended notify script to add the priority in "$4" when calling a notify script. This is important in labs and datacenters when systems can display the priority on a front LCD, because it allows workers to carefully operate without causing unexpected reelections. * VRRP : Willy Tarreau extended interface tracking framework to let interface tracking change the priority by adding a "weight" parameter. If the weight is positive, it will be added to the priority when the interface is UP. If the weight is negative, it will be subtracted from the priority when the interface is down. If the weight is zero (default), a down interface will switch the instance to the FAULT state. * VRRP : Willy Tarreau added a new "vrrp_script" section to monitor local processes or do any type of local processing to decide whether the machine is in good enough health to be elected as master. A same script will be run once for all instances which monitor it. If no instance use it, it will not be run, so that it's safe to declare a lot of useful scripts. A weight is associated to the script result. If the weight is positive, it will be added to the priority when the result is OK (exit 0). If the weight is negative, it will be subtracted from the priority when the result is KO (exit != 0). If the weight is zero, the script will not be monitored. The default value is 2. * VRRP : Willy Tarreau extended vrrp scheduler so that when a VRRP is part of a SYNC group, it must not use floating priorities, otherwise this may lead to infinite re-election after every advertisement because some VRRPs will announce higher prios than the peer, while others will announce lower prios. The solution is to set all weights to 0 to enable standard interface tracking, and to disable the update prio thread if VRRP SYNC is enabled on a VRRP. * VRRP : Willy Tarreau added some documentation and examples for the brand new VRRP tracking mechanisms. * VRRP : Ranko Zivojnovic, fixed vrrp scheduler to execute notify* scripts in transition from the failed state to the backup state. * Nick Couchman, , added support for real server upper and lower thresholds. This allows you to set a minimum and maximum number of connections to each real server using the "uthreshold" (maximum) and "lthreshold" (minimum) options in the real_server section of the configuration file. * Chris Caputo, extended autoconf script to support recent move of UTS_RELEASE from linux/version.h to linux/utsrelease.h. * Chris Caputo, extended ipvswrapper 2.4 code to support misc_dynamic weight. 2006-03-09 Alexandre Cassen * keepalived-1.1.12 released. * VRRP : Christophe Varoqui, extended VRRP framework to use virtual_router_id as syncid in LVS mcast datagram while using LVS syncd in VRRP instance. * Kevin Lindsay, and Christophe Varoqui, fixed SSL checker to properly use openssl when dealing with asynchronous stream handling. Kevin fixed asynchronous handling during connection stage while Christophe fixed stream handling after connection stage. * Kjetil Torgrim Homme, extended keepalived spec file to cleanly compile on RedHat enterprise 3 and 4. * Heinz Knutzen, fixed SMTP checker to overwrite default_host while parsing configuration file. A SMTP_CHECK without a "host" section should use the ip of the current real server as default. 2005-03-01 Alexandre Cassen * keepalived-1.1.11 released. * Asier Llano Palacios, extended autoconf script to support cross-compilation. * Kevin Lindsay, and I fixed a missing bitwise negation while removing signal from global signal mask. Set this operation before handler is called. This assume that bitwise negation is an atomic code generated from compiler. Since gcc 3.3 this is true. * VRRP : extended ipaddress and iproutes code to return if vip or vroutes is referencing an unknown interface. 2005-02-15 Alexandre Cassen * keepalived-1.1.10 released. * VRRP : While restoring interface, release iproutes before ipaddresses. Routing daemons needs that order for netlink reflection channel. * VRRP : Bin Guo, fixed a memory leak while calling script_open. * Kevin Lindsay, fixed some buffer overruns, NULL pointer and dangling pointer references. * Kevin Lindsay, redisigned signal handling. When a signal occurs, a global signal_mask is modified. In the main loop there is a checked to see if the signal_mask has any pending signals. The appropriate signal handler is then run at this time. This is to prevent races when modifying linked lists. * Kevin Lindsay, fixed shadowed declarations. * Christophe Varoqui, and I Extended libipvs-2.6 to support syncd zombies handling. Since ip_vs_sync.c kernel code no longer handle waitpid() we fork a child before any ipvs syncd operation in order to workaround zombies generation. * John Ferlito, and I Fixed a scheduling race condition while working with low timers. * Updated check_http and check_ssl to use non-blocking socket. * Fixed some race conditions while reloading configuration. Prevent against list gardening if list is empty ! * Fixed recursive configuration parsing function to be clean with stack. Only one recursion level. * Some cosmetics cleanup in Makefiles. 2005-02-07 Alexandre Cassen * keepalived-1.1.9 released. * VRRP : Chris Caputo, updated keepalived manpage for nopreempt and preempt_delay. * VRRP : Fixed an issue while releasing vrrp socket pool... Just release pool one time ! * VRRP : Fixed netlink framework to properly save netlink socket flags while setting blocking flags. * VRRP : Fixed a regression introduced with previous release while hashing vrrp fd bucket into fd hash index. * Patrick Boutilier, fixed an issue in the extract_html function. Read the full html header. * Chris Caputo, and I fixed compilation issue while using --enable-debug configuration option. * Extended both VRRP and Healthchecker framework to support debugging flags. * Removed the watchdog framework. Since scheduling framework support child, we register a child thread for both process VRRP & Healthcheck. When child die or stop prematuraly this launch scheduling callback previously registered. Watchdog is now handled by signaling. (credit goes to Kevin Lindsay, for nice idea). * Some cosmetics cleanup. 2005-01-25 Alexandre Cassen * keepalived-1.1.8 released. * VRRP : Chris Caputo, added "dont_track_primary" vrrp_instance keyword which tells keepalived to ignore VRRP interface faults. Can be useful on setup where two routers are connected directly to each other on the interface used for VRRP. Without this feature the link down caused by one router crashing would also inspire the other router to lose (or not gain) MASTER state, since it was also tracking link status. * VRRP : Chris Caputo, added "nopreempt" which overrides the VRRP RFC preemption default. This replaces the "preempt" keyword which was not fully implemented. "preempt" is kept around for backward compatibility but is deprecated. * VRRP : Chris Caputo, added "preempt_delay" which allows one to specify number of seconds after startup until VRRP preemption. (range 0 to 1,000 seconds) this is useful because sometimes when a machine recovers it takes a while for it to become usable, such as when it is a router and BGP sessions need to come back up. * Chris Caputo, made it so there is a useful "Date:" in SMTP alert emails. * VRRP : Chris Caputo, . In debug output log gratuitous ARPs with actual IP addresses being ARPed. * VRRP : Chris Caputo, . If started with "--dont-release-vrrp" then try to remove addresses even if we didn't add them during the current run, when it makes sense to do so. * VRRP : Chris Caputo, added a missing free_vrrp_buffer() during VRRP stop. * VRRP : Kees Bos, fixed VRRP sanity check to perform checksum computation over incoming packet and not local router instance memory representation => Better to log 'invalid vip count' instead of 'Invalid vrrp checksum' when the number of configured vips differ in the master and backup server :) * VRRP : Release socket pool during daemon stop and reload * VRRP : Refresh socket pool during reload * VRRP : Extended netlink framework to support blocking operation. During initialization, set blocking netlink channel to wait responses from kernel while parsing result. Kernel netlink reflection are still handled using non-blocking. * Jeremy Rumpf, added SMTP checker. It take a special care of smtp server return code. * Merged genhash man page * Chris Caputo, added "misc_dynamic" to a MISC_CHECK which makes it so a script can adjust the weight of a real server. * Fixed some assertion issue in memory framework. * Use router_id instead of lvs_id in the global_def configuration block (lvs_id kept for backward compatibility). * Ronald Wahl , fixed declarations to be only in includes files. * Ronald Wahl , moved the definition of variables to C files * Ronald Wahl and I fixed scanning for header/body separator in HTTP protocol * Ronald Wahl replaced memcpy by memmove where source & destination may overlap * Extended checker API to only register checkers when checker callback is defined. * Jacob Rief, fixed openlog to take care of configured log facility. * Move in_csum to util file. * Extended libraries to support some new facilities (list and vector). * Extended scheduler I/O to use timer decalred on the stack. * Some cosmetics changes. 2004-04-05 Alexandre Cassen * keepalived-1.1.7 released. * Jacob Rief, added target tarball into root Makefile to facilitate packaging (rpm & tarball). * Jacob Rief, and I unified version handling. Now only the root file VERSION is used by configure to add VERSION_STRING via config.h.in. Added VERSION_DATE included into the VERSION_STRING that reflect the building date into the version banner. * Andres Salomon, wrote the genhash manpage. * VRRP : Added ipvs_start() and ipvs_stop() calls during vrrp child start and stop stage. * Added some assertion test in memory framework to not allocate bucket if no more place. This option is only used if compiled with debug flags. * Some cosmetics patch in Makefiles and autoconf script. 2004-02-23 Alexandre Cassen * keepalived-1.1.6 released. * VRRP : Fixed scheduling timer update. Global scheduling timer is updated before each thread registering and after scheduling I/O MUX. Since is needed to take care of scheduling jitter introduced by overhead (VRRP is using low low timer so more sensitive to overhead). Thanks to Nathan Neulinger, for his quick feedback debugging time. * VRRP : Nathan Neulinger, updated vrrp dropping strategy to not reply to incoming bogus adverts. Since this can introduce flooding loop, bogus adverts are now simply silently dropped. * VRRP : Fixed a linkbeat issue while polling NIC flags. * Updated autoconf and Makefile to support 2.6 kernel IPVS code. For code readability, created 2 differents libipvs for 2.4 and 2.6 kernel . Fixed autoconf generated warning. * Extended ipvswrapper to support shared buffer user rule. This increase performances by limiting memory allocation. OTOH, created two new ipvs helpers ipvs_start & ipvs_stop to initialize ipvs subsystem. * Andres Salomon, made some cosmetics update in Makefiles to support $(DESTDIR) and $(BIN)/$(EXEC) path split. 2004-01-25 Alexandre Cassen * keepalived-1.1.5 released. * Joseph Mack, wrote keeplived manpages in doc/man/man5/keepalived.conf.5 and doc/man/man8/keepalived.8. * VRRP : Tsuji Akira, fixed a length issue while testing password field for auth_pass method. * VRRP : Willy Tarreau, fixed a quick loop in the watchdog timer thread. * VRRP : Willy Tarreau, extended scheduler to support stable scheduling time. There is now, only one time source updated before and after scheduling event. This solve sliding timer observed on some env, also known as periodically flapping issue (sometime a VRRP election is forced). * VRRP : Willy Tarreau, updated the default media link failure detection strategy to perform a ioctl ifflags even if NIC driver are supporting MII or ETHTOOL. Some buggy drivers need this. Anyway the linkwatch patch still the best solution to support efficient and scalable media link failure detection. * Some cosmetics clean-up, removed some dead files, updated autoconf and Makefile prototypes to support dependencies libs like kerberos for RedHat/Fedora distro. To compile keepalived properly on redhat 9 box, for example, run : export CPPFLAGS="-I/usr/kerberos/include" && ./configure Renamed keywords lb_kind to lvs_method and ld_algo to lvs_sched. For compatibility reasons, old keywords are still available. 2003-12-29 Alexandre Cassen * keepalived-1.1.4 released. * Refresh autoconf script to use autoconf 2.5. * Extended the autoconf script to support linkwatch kernel detection. * To work-around the SMP forking bug, added support to two new daemon starting options : --vrrp -P Only run with VRRP subsystem. --check -C Only run with Health-checker subsystem. Those options extend daemon design to support VRRP & heathchecking subsystem selection. You can now run two Keepalived daemon one invoqued with --vrrp and the other with --check. That way we workaround the forking issue by running one daemon per subsystem. * Tiddy cleanup in the daemon code. * VRRP : Extended the link media failure detection to support asynchronous NIC MII polling. The design use now, one dedicated polling thread per NIC. This reduce scheduling jitter by this way. * VRRP : Added support to kernel linkwatch subsystem. This patch that you will find a copy on the Keepalived website for the kernel 2.4 branch, provides kernel netlink broadcast events drived by NIC link media state event. That way we move from a polling design to an event design. Link events are received throught a kernel netlink broadcast socket in the userspace land. So, NIC media link failure detection is now provided by kernel netlink reflection. You can read the paper attached with the patch for indepth explanations. * VRRP : fixed timer computation to prevent against negative value. 2003-09-29 Alexandre Cassen * keepalived-1.1.3 released. * Stephan von Krawczynski, extended ip address framework to support broadcast address selection. * Extended the scheduling framework to support plain 'long' timer. Visited the layer4 framework to support this new scheduling scheme. Reviewed the checkers and VRRP framework to support long timer. * VRRP : Removed the timer micro adjust call. Its use is obsolete with the new scheduling 'long' timer support. * Jacob Rief, and I added support log level selection for main daemon. A new command line argument has been created : --log-facility -S 0-7 Set syslog facility to LOG_LOCAL[0-7]. (default=LOG_DAEMON) * Extended the HTTP checker to support non blocking read while processing stream. NONBLOCK flags is set before read operation to catch EAGAIN error. * VRRP : Diego Rivera, and I fixed a notify issue while building notify exec string. * VRRP : Diego Rivera, and I extended FSM to support BACKUP state notifiers and smtp_alert call during VRRP initialization. * Jan Vanhercke, and I extended scheduling timer computation to support micro-sec second overlap. Extended the whole scheduling framework to support this scheduling scheme while computing thread timers. * Fixed scheduling framework to support child thread timers while computing global scheduling timer. 2003-09-07 Alexandre Cassen * keepalived-1.1.2 released. * Dominik Vogt, and I extended checker framework to support multiple checkers per realserver. Each checker own a uniq id, each realserver own a list of checkers id. Realserver is considered down if one of the checkers fails. * Dominik Vogt, extended list library to support free_list_element. * Dominik Vogt, and I extended ipwrapper to support multiple checkers test. Created a checker state updater helper function to perform realserver state according to checker state. * Dominik Vogt, extended all checkers code to support multiple checker design (to not perform server state according a single checkers test). * Tobias Klausmann, and I extended layer4 framework to support socket binding to a specific ip address before calling connect(). Extended the TCP, HTTP and SSL checker to support binding selection, creating a new checker keyword named "bindto". look at doc/keepalived.conf.SYNOPSIS for more informations. * VRRP : Extended the ethtool code to be selected only if ETHTOOL_GLINK is available. This is useful for s/390 zSeries users :) since zSerie 2.4 kernel doesn't support ethtool extension. * VRRP : Gatis Peisenieks, fixed IPSEC-AH code to exclude ip header id filed while computing AH digest. Fixed AH sequence number to be set in network byte order. * VRRP : Fixed a bug in the static_ipaddress block that caused a noisy crashing startup. * VRRP : Kjetil Torgrim Homme, and I fixed a daemon crash while reloading configuration due to a vrrp_buffer not freed. * VRRP : Review the watchdog calling location. watchdog listener is reinitialized during a daemon reload. * VRRP : Diego Rivera, extended notify framework to support simple notify script call. Created a new keyword "notify", for both vrrp_instance and vrrp_sync_group. If configured, this notify script is called after FSM state transition notify scripts. look at doc/keepalived.conf.SYNOPSIS for more informations. * Review the checker watchdog calling location like VRRP. * Fixed code selection to exclude VRRP dependencies if code is configured without VRRP framework. * Extended memory lib free function to reset memory location to NULL. * Diego Rivera, extended global parser to support default handlers for lvs_id, smtp_server, smtp_connection_timeout and email_from. default values are : o lvs_id : box local name o smtp_server : localhost o email_from : uid@box_local_name o smtp_connection_timeout : 30s 2003-07-24 Alexandre Cassen * keepalived-1.1.1 released. * VRRP : Fixed an issue while reloading configuration. Fixed a dereferencing pointer. * Fixed misc checker to perform server state according to checker result !!! 2003-07-22 Alexandre Cassen * keepalived-1.1.0 released. * The release focus is : "High Performance" * Name cleanup for the healthchecking directory. use check instead of healthcheck to be in conformance with watchdog and global software architecture. * updated the SYNOPSIS file for documenting the table arg inside virtual/static_routes declaration. You can set routes refering to a specific TABLE-ID. * Added a dummy debug var in the genhash declaration code to support compilation when compilation is done with debug flag. * Added a set flag inside the real_server declaration correctly relfect the IPVS topology when inhibit_on_failure is used. * fixed a daemon.h include depandency on signal.h * VRRP : Added support to a global shared buffer for incoming advert handling. A new buffer is no longer allocated each time processing incoming advert, instead a shared room is used. * VRRP : Added support to pre-allocated shared buffer for outgoing adverts. Each vrrp instance use a 'one time' allocated buffer instead of a 'all time' one. * VRRP : Extended the socket pool design to support shared fd for the outbound channel. Now, socket pool create a sending socket and affect the fd returned to vrrp instances. This forces instances to use a shared socket instead of creating new socket for each outgoing adverts. The error detection is based on the incoming socket, so that outgoing socket is not created as long as incoming socket can not be created. * Added support to netlink ipaddress as global keyword "static_ipaddress". look at doc/samples/keepalived.conf.static_ipaddress. IP addresses specified into this block will be added during daemon bootstrap and removed during daemon shutdown. Differential conf parsing is enabled for this block, removing/adding static_ipaddress can be done on the fly sending SIGHUP signal to daemon. * VRRP : Extended track_interface to support multiple interface tracking. For those familiar with Nokia monitored circuit, this extention provide the same functionality. look at doc/samples/keepalived.conf.track_interface. * VRRP : The VRRP instance lookup framework has been extended to use a o(1) scheduling design. Rewrote the whole instance lookup to use o(1) lookup instead of previous o(n^2). When receiving incoming adverts vrrp_scheduler performs a lookup over the VRID received to get local instance representation. Since the internal instance representation is an non-sorted linked list, then we run a lookup at o(n^2) complexity that introduce lantency and scheduling jitter side effect when runing large number of instances. To avoid this limitation a static hash table of 255 buckets were created. Since lookup is performed over VRID and since VRID is 8bit fixed, then the hashkey will be VRID. In order to extend code the hashkey is based on incoming fd too. Internally, a NIC is represented by a 2 fds : sending socket and receiving socket. Those fds are NIC specific so we are using them as a hash table lookup collision resolver. With this design we can now use the same VRID on different NICs. The collision design is a linked list so lookup is o(n^2) but due to low number of entries we can consider o(1) speed. But to reach best perf, differents VRID on all instance must be used. The design can be sumed by : VRID hash table : +---+---+---+---+---+---+.........+-----+ | 1 | 2 | 3 | 4 | 5 | 6 |.........| 255 | +---+---+---+---+---+---+.........+-----+ | | +---+ +---+ |fd3| |fd1| +---+ +---+ | +---+ |fd5| +---+ This hash table is filled during configuration parsing and VRRP instances are not duplicated but dynamically pointed to optimize memory. * VRRP : The VRRP synchronization group lookup has been extended. During bootstrap a VRRP instance index is built upon sync_group instance name. This extension speed up synchronization since while synchronizing it perfoms the instance index instead of lookup by instance_name. The previous synchornization code has been rewritten to use this 'list visiting' design for FAULT/BACKUP/MASTER states synchronization. * VRRP : Optimized the vrrp_timer_vrid_timeout(...) to speed up vrid lookup over timeouted fd using a one pass lookup. * Bradley Baetz, extended the scheduler framework to support child process handling. Adding support to new thread child facility for handling child processes, and modifying the scheduling select loop & signal handling to catch SIGCHLD, and call the appropriate process. * Bradley Baetz, fixed the misc_check healthchecker using new thread child scheduling facility. Introduced a new keyword "misc_timeout" to kill processes which take too long time (default is delay_loop). SIGKILL is send to processes if they take too long time to shutdown. * Bradley Baetz, extended daemon framework to block SIGCHLD to only receive it whn its unblocked in the scheduling loop. * Extended healthchecker delay_loop to support long delay (ie: >1000s). * VRRP : Added support to a shared kernel netlink command channel for setting ip address and routes. * Extended the genhash code to support verbose output selection. command arg '-v' will generate a very verbose output. * VRRP : Extended the logging code to select verbose log output or not. This selection is done by passing the '-D' option to command line while starting daemon. By default the output is silent. * VRRP : Extended the gratuitous ARP framework to support shared buffer and shared socket. This increase performances for instances owning a bunch of VIP. * VRRP : Extended the scheduling timer computation to support timer auto-recalibrating. While computing next instance timer, the scheduler will substract the time taken by previous advert handling. This provide software overhead adaptation. The recalibration is performed over usec timer to not pertube global scheduler. * VRRP : Fixed a gratuitous ARP issue. Extended the ipaddress framework to point directly to interface reflected by netlink channel instead of storing device index. Extended the gratuitous ARP code to use new ipaddress structure and for sending garp over device ipaddess belong to. Needed if you run an instance on one device interface and set VRRP VIP on different interface. * Extended watchdog framework to support polling delay selection via daemon command line. Created two new cmdline options : --wdog-vrrp -R Define VRRP watchdog polling delay. (default=5s) --wdog-check -H Define checkers watchdog polling delay. (default=5s) * Extended SMTP code to support bigger buffer while processing remote mta messages. * Erik Barker, extended initscript to support native redhat init functions. * Extended the autoconf scripts and Makefile(s) to support code profiling. New configure option : --enable-profile * list library has been extended to support multi-sized list & specific element deletion. Extended to return when list is empty. This reduce duplicated code to test is list is empty while processing. * VRRP : Extended VRRP scheduler to support fd hash table design. Speed up instance lookup while computing instance sands. This offer o(1) design if we consider limited number of instances per device. * VRRP : Extended vrrp new socket creation to replace refreshed instance fd into fd hash table index. * VRRP : Extended vrrp framework to support blank virtual_ipaddress block, can be usefull if someone want to use just the VRRP advert as hello monitoring channel. * Some code cleaning. 2003-05-12 Alexandre Cassen * keepalived-1.0.3 released. * This release has been sponsorized by : Tiscover AG, Please visit sponsor homepage. I would just like to thanks their IT team for interresting design discussions and testing time, especially Jacob Rief. * This release consist of a major daemon re-design to increase security and availability of Keepalived. The daemon has been splitted into 3 distinct process. The global design is based on a minimalistic parent process responsible for monitoring its forked children process. Then 2 children process, one responsible for VRRP framework and the other for healthchecking. Each children process has its own scheduling I/O multiplexer, that way VRRP scheduling jitter is optimized since VRRP scheduling must be more sensible than healthcheckers. On the other hand this splitted design minimalize for healthchecking the usage of foreign librairies and minimalize its own action down to and idle mainloop in order to avoid malfunctions caused by itself. The parent process monitoring framework has been called watchdog, the design is : each children process open an accept unix domain socket, then while daemon bootstrap, parent process connect to those unix domain socket and send periodic (5s) hello packets to children. If parent cannot send hello packet to remote connected unix domain socket it simply restart children process. This watchdog design offer 2 benefit, first of all hello packets sent from parent process to remote connected children is done throught I/O multiplexer scheduler that way it can detect deadloop in the children scheduling framework. The second benefit is brought by the uses of sysV signal to detect dead children. When running you will see in process list : PID 111 keepalived <-- parent process monitoring child activity 112 \_ keepalived <-- VRRP children 113 \_ keepalived <-- Healthchecking children * Parent : Created a global data and global keyword parser structure. * Healthcheck framework : Defined check_conf_data to handle related checker data structures. Created specific checker framework parser. * VRRP framework : Defined vrrp_conf_data to handle related vrrp data structures. Created specific vrrp framework parser. * Each child process has its own syslog facility. VRRP use LOG_LOCAL1 and Healthchecker LOG_LOCAL2. To split log you can so configure your syslog to log both facilities in a different logfile. * Modularized the configuration parser to limit code duplication. * Created modularized software watchdog. * Extended the recursive stream parser to use sublevel detection while stream processing. Used to skip end-of-block handling if still at keyword root level to prevent against end parsing if unknown block is parsed. * Extended pidfile framework to be more generic. * Extended memory framework to log specific child data. * Fixed a virtual_server_group issue while healthchecker bringing back real_servers. Modularized virtual_server_group API. * Fixed a virtual_server_group issue will reloading configuration. Remove vsgname test from the VS_ISEQ macro. strcmp(...) comparing null pointer... this must have been done in libc :) * ipwrapper : set alive flag after ipvs_cmd(...) has been performed. * VRRP : Extended the netlink framework to support SCOPE selection for both ipaddress and routes fonctionnalities. SCOPE available are site, link, host, nowhere & global. Default value is set to global. look at doc/keepalived.conf.SYNOPSIS for more informations. * Renamed doc/samples/keepalived.conf.routes to doc/samples/keepalived.conf.vrrp.routes. * Updated Makefile include dependencies. 2003-04-14 Alexandre Cassen * keepalived-1.0.2 released. * This release has been sponsorized by : edNET, Please visit sponsor homepage and thanks to them for supporting keepalived project. * Added support to virtual_server_group so that a virtual_server can be either an IP:PORT, a fwmark or group. A group is a set of virtual_server IP:PORT, IP range and fwmark. So, now a real_server can be part of multiple virtual_server without launching multiple time the same healthchecker that finaly flood real_server. This extension is useful for big ISP/ASP configuration using many virtual_server. look at doc/samples/keepalived.conf.virtual_server_group. * Extended differential configuration parser to support diff virtual_server_group entries keeping current entry state as persistent (weight, conn, ...) big work here... * Added support to IP range declaration for virtual_server_group. The IP range has the notation XXX.YYY.ZZZ.WWW-VVV. This will set IPVS virtual_server from WWW to VVV monotonaly incremented by one. look at doc/samples/keepalived.conf.virtual_server_group. * Dominik Vogt, enhanced SIGCHLD handler to reap all zombie child processes. * Created a generic allocation value block with callback handler for block parsing. This remove duplicated code in parser. * VRRP : Jan Holmberg, extended the virtual_routes and static_routes to support source route selection (netlink RTA_PREFSRC). look at doc/samples/keepalived.conf.routes. * Some cosmetics patches to reduce code duplication. 003-03-17 Alexandre Cassen * keepalived-1.0.1 released. * This release has been sponsorized by : Creative Internet Techniques, Please visit sponsor homepage, open minded people here ! * Fixed some Makefile and autoconf code dependence issues. * Move keepalived.conf.SYNOPSIS and samples into "doc" directory. * Enhanced HTTP|SSL check to support large url. Get buffer request is now 2KBytes. * Removed \n in healthchecker smtp_alert call. This cause some troubles with MTA like qmail. Thanks go to John Koyle, . * Added support to netlink route as global keyword "static_routes". look at doc/samples/keepalived.conf.routes. Routes specified into this block will be added during daemon bootstrap and removed during daemon shutdown. Differential conf parsing is enabled for this block, removing/adding static_route can be done on the fly sending SIGHUP signal to daemon. * VRRP : Added support to "virtual_routes". This is the same as virtual_address. Those routes are set when VRRP instance enter MASTER state and removed otherwise. Differential conf parsing is enabled for this block. This concept extend VRRP and bring dynamic routing as a "route takeover" concept. * VRRP : Rewrote the VRRP vip handling to use template lib list structure. VIP and E-VIP are no longer a simple array reallocated. List library is used to limite code duplication. * VRRP : Extended virtual_ipaddres and virtual_ipaddress_excluded block to support "dev" specification. So that a VIP can be set to a specific interface instead of default runing VRRP instance interface. * VRRP : Added support to "track_interface". Interesting for use with vlan interface. The concept here is to drive VRRP FSM according do both "interface" and "track_interface" state. If tracked interface is down or instance interface is down then VRRP instance transit to FAULT state. For use with vlan, add track to interface vlan belong to. Look at doc/sample/keepalived.conf.track_interface for sample. doc/keepalived.conf.SYNOPSIS for configuration details. * VRRP : Extended FSM FAULT state to keep in fault if track_interface still fault. * VRRP : Extended sync group design to test if group is unary or not. * Some code cleaning and cosmetics enhancements. 2003-01-06 Alexandre Cassen * keepalived-1.0.0 released. * After fixed all bugs users reported during 2 months, I am glad to announce the first STABLE production ready Keepalived release. * Rename keepalived.init to keepalived RedHat startup script. Fixed some issues to be RedHat release generic. Thanks go to Jeroen Simonetti & Jason Gilbert * Jason Gilbert, cleaned keepalived.spec. * Added support to "ha_suspend" for healthcheckers. This option, if set, inform Keepalived to active/suspend checkers according to netlink IP address information reflection. If one IP is removed and this is a virtual_server VIP then the healthcheckers corresponding will be desactivated. (and reciprocity). * Added support to "notify_up" & "notify_down" for realserver config. These options specify a script to be run according to healthchecker activity. If healthchecking fails then "notify_down" script is launched (and reciprocity for healthcheck succeed). This can be usefull for global monitoring system, to send alert to Unicenter TNG or HPOV. * Set default realserver weight to 1. So, realserver will be active if no weight is specified into the configuration file. * Review the layer4.c/tcp_socket_state to return connection in progress only if SOL_SOCKET/SO_ERROR return EINPROGRESS. Thanks go to Mark Weaver, * Reviewed the global SIGCHLD handler to not suspend execution of the calling process if status is not immediately available for one of the child processes. This remove zombies by reaping. * Extended the parser.c/set_value() code to accept encapsulated quoted string. * Review SMTP DBG() message to LOG_INFO message for more verbose error handling. * Review the check_tcp.c/check_http.c logging messages to be more detailed. * Review the check_tcp.c/check_http.c retry facility to fixes some stalled issues. * VRRP : Added support to sync_group smtp notification in addition to the per instances approach. * VRRP : Fixed some IPSEC-AH seq_num synchronizations issues. Force seq_num sync if vrrp instance is linked to a group. * VRRP : In BACKUP state, force a new MASTER election is received adv. has a lower priority as locale instance. * VRRP : vrrp.c/vrrp_state_master_rx(), sync IPSEC-AH seq_num counter (decrement) if receiving higher prio advert in MASTER state. * VRRP : Reviewed the TSM to be fully filled. Extended speed-up synchronization handling MASTER sync if group is not already synced. * VRRP : Leaving fault state, force MASTER transition is received adv priority is lower than locale. * VRRP : Extended the parser to not be borred with sync_group declaration position in the conf file. vrrp_sync_group can be declared before or after vrrp_instance. Done by adding a reverse instance lookup during parsing. * VRRP : sync_master_election cleanup. * Some cosmetics patches. * Created the keepalived/samples/keepalived.conf.SYNOPSIS to describe all keywords available. 2002-11-20 Alexandre Cassen * keepalived-0.7.6 released. * Created a common library for code modularization. This lib will be used by all Keepalived components (genhash + Keepalived) to reduce repeated and duplicated code. * Rewrote the genhash utility using the common lib. The design is similar to Keepalived core design. * Reviewed the autoconf and Makefiles for new code architecture. * Created a html utility lib for HTTP headers manipulations. * Extended the CHECK_HTTP and CHECK_SSL checkers to support remote webserver HTTP header status_code. HTTP status_code is parsed according to rfc2616.6.1. The keyword created for the new feature is "status_code" inside and "url" declaration. "status_code" feature can be mixed with "digest" feature. See the samples directory keepalived/samples/keepalived.conf.status_code for example. * Review the CHECK_HTTP and CHECK_SSL MD5SUM code to use a common stream handling function. * Matthijs van der Klip, and I fixed a bug into the HTTP/SSL code that close the socket fd even if remote webserver has not been connected. As a result of fact, next socket created were imediatly closed. As a side effect, this altered the SMTP notification when remote webserver checked fall. No SMTP notification were sent if webserver were detected DOWN. Thanks to Matthijs for time debugging and investigation. * VRRP : Rewrote the previous Gratuitous ARP facility. Created a lib (vrrp_arp.c) dealing with PF_PACKET-SOCK_RAW-ETH_P_RARP and sockaddr_ll. * VRRP : Some cosmetics patch for messages logging. * VRRP : Fixed an issue during VRRP packet building, appending VRRP VIPs to the VRRP packet in the network order form. * VRRP : Reviewed the previous VRRP packet building process to not create the ARP header. Removec the previous hacky PF_PACKET-SOCK_PACKET-0x300 to use AF_INET-SOCK_RAW-PROTO to leave kernel appending ARP header since code doesn t currently support VRRP VMAC. * VRRP : Rewrote the previous vrrp_send_pkt() function to deal with sendmsg(). optimization lazzyness :) * VRRP : Extended the interfaces library to support common utility functions (if_setsockopt_hdrincl, if_setsockopt_bindtodevice, ...) * VRRP : Finally extend the code to support VRRP IPSEC-AH authentication method. Created a IPSEC-AH seq_number syncrhonization mecanism during VRRP MASTER/BACKUP elections. * VRRP : Extended the VRRP TSM to speed up instances syncrhonization during FAULT->BACKUP & FAULT->MASTER state transition. * Some cosmetics patches. This release is proposed as a 1.0.0 STABLE release candidate. 2002-09-17 Alexandre Cassen * keepalived-0.7.1 released. * Fixed a MISC_CHECK issue when registering next timer checker. Must register a new timer thread before forking process. This imply for the user the extra script call must not execute in more than checker->vs->delay_loop. * Extented the ipfwwrapper (for LVS kernel 2.2) to not set ipchains rules if nat_mask is not specified in the configuration file. * VRRP : Added support to delayed gratuitous ARP send. When one instance enter to MASTER state a timer thread is registered. The default delay is 5secs. This delay is configurable per vrrp instance and handle the 'garp_master_delay' keyword. This delay refer to the delay after MASTER state transition we want to launch gratuitous ARP. * VRRP : Force health checker enable flag if VRRP framework is not selected. * VRRP : Review the gratuitous ARP helper function to only send gratuitous ARP if VRRP VIPs are set. * VRRP : Review the FSM to eliminate stalled flapping loop. The state transition diagram implemented is : +---------------+ +----------------| |----------------+ | | Fault | | | +------------>| |<------------+ | | | +---------------+ | | | | | | | | | V | | | | +---------------+ | | | | +--------->| |<---------+ | | | | | | Initialize | | | | | | | +-------| |-------+ | | | | | | | +---------------+ | | | | | | | | | | | | V | | V V | | V +---------------+ +---------------+ | |---------------------->| | | Master | | Backup | | |<----------------------| | +---------------+ +---------------+ The state DUMMY_MASTER state has been removed since it is a fake. * VRRP : In order to handle all possible state transition, a Transition State Matrix design (TSM) has been added. This matrix defines transition state handlers for VRRP sync group extension. The TSM implemented is (cf: vrrp_scheduler.c for more informations) : \ E | B | M | F | S \ | | | | ------+-----+-----+-----+ Legend: B | x 1 2 | B: VRRP BACKUP state ------+ | M: VRRP MASTER state M | 3 x 4 | F: VRRP FAULT state ------+ | S: VRRP start state (before transition) F | 5 6 x | E: VRRP end state (after transition) ------+-----------------+ [1..6]: Handler functions. * VRRP : Set ms_down_timer to 3 * advert_int + TIMER_SKEW when leaving MASTER state. * VRRP : In MASTER state, when incoming advert match or FAULT state is requested then force leaving MASTER state transition. (review the previous election approach). * VRRP : Optimized the leave FAULT state transition. Directly coded into the FSM for speed up recovery or code readability. * VRRP : Extended smtp notifier for BACKUP state. Review the MASTER state notification to only notify when VIPs are set. * some cosmetics patches. * Adam Fletcher, created the 'Keepalived+LVS NAT HOWTO' 2002-08-05 Alexandre Cassen * keepalived-0.6.10 released. * Fixed a faked flag during VRRP VIP set. Updated the IP address set flag to reflect netlink return code. * Fixed an autoconf issue during selection of VRRP framework. 2002-07-31 Alexandre Cassen * keepalived-0.6.9 released. * Fixe some code dependence selection during compilation. If autoconf netlink probe fails then unset VRRP code. * Cleanup daemon lib. Added some logging info for the daemon processing, removed some repeated code part. * Added 2 new daemon arguments : --dont-release-vrrp : Dont remove VRRP VIPs on daemon stop --dont-release-ipvs : Dont remove IPVS topology on daemon stop * Review the global scheduling process to clear FD queues on master thread destroy. * Fixed a forking issue in the MISC_CHECK. * Review IPVS wrapper functions to use allocated IPVS rules instead of static referencing pointer. * Fixed the IPVS wrapper to delete IPVS entries according to their 'alive' state. * Added IPVS support to alive flag for VS entries. * Rewrote the previous main.c to support configuration reload on the fly. Extented signal handling to register a conf reload_thread on SIGHUP. The software design used here is a dynamic differential conf file reloading framework. This design offer key decision to add/remove new/old entries to/from low-level framework: IPVS topology and netlink IP addresses entries. This design reduce to the max the global service interruption since only negative diff entries are removed. For VRRP config reload on the fly, if you plan to add/remove many VIPs consider VIP declaration into the virtual_ipaddress_excluded since they are not present into VRRP adverts. * Review the keepalived.init script to support restart and reload arguments. * Fixed some typo issues. 2002-07-16 Alexandre Cassen * keepalived-0.6.8 released. * Alex Kramarov, & Remi Nivet, reported an assertion error during smtp notification process. The assertion caused a bad file descriptor registration during in_progress connection handling. Fixed registering an event thread calling upper level SMTP protocol in_progress connection handler. So the SMTP stream handlers use global I/O multiplexer on connection success. * Benoit Gaussen, and I added support to "inhibit" feature. Added a new keyword called "inhibit_on_failure" for real_server declaration. If specified the real_server will not be removed from the IPVS topology if real_server fail according to checker result. Instead of removing the entry from IPVS topology, the corresponding real_server weight will be set to 0. When real_server will be back, then weight will be set back to original value. See sample directory for example. * Added support to IP_MASQ_CMD_SET_DEST for 2.2 krnl and IP_VS_SO_SET_EDITDEST for 2.4 IPVS code to provide support to "inhibit" feature. * Review Makefile.in to exit on compilation error. * Extended autconf script to check for kernel netlink support. 2002-07-12 Alexandre Cassen * keepalived-0.6.7 released. * Rewrote the previous SMTP notification framework. New code use a strong multi-threaded FSM design. * Moved the SMTP get_local_name() into utils.c * IPVS : updated the code to support IPVS_SVC_PERSISTENT_TIMEOUT. Introduced into the new libipvs coming with ipvs-1.0.4. * VRRP : Extended the mcast membership subscription to handle more robust mcast subscription errors. Removed the previous ugly stalling sleeping call retry for membership subscription. Membership subscriptions are now multi-threaded to not degrade global scheduling timer. * VRRP : Remi Nivet, pointed out a buffer overflow during the sending advert interface binding process. * Some more cosmetics patches. 2002-07-05 Alexandre Cassen * keepalived-0.6.6 released. * added indentation style .indent.pro * Review the previous source tree. Splitted the code into functional subdirs. Added multi-level automake scripts. The source tree looks like : . |-- bin |-- genhash |-- keepalived | |-- core | |-- etc | | |-- init.d | | `-- keepalived | |-- healthcheck | |-- include | |-- libipfwc | |-- libipvs | |-- samples | `-- vrrp `-- lib * Refine autoconf/automake scripts. Added automake support to libipvs and libipfwc. Added code selection compilation for libipvs and libipfwc. * Review Makefile(s) to use more convenient facilities like distclean, ... * Review the Makefile(s) code dependencies. * Added support to modprobe_ipvs if the ip_vs.o module is not loaded. If modprobe fails then IPVS is assumed unavailable. * Refine the IPVS wrapper to be more tolerant. When a VS or RS is already configured don t stop the daemon. The daemon is stopped only on critical IPVS errors. * VRRP : Review the bootstrap sequence to start daemon even if one of the instance want to run on an interface administratively shut. Added extension to FSM to force transition to FAULT state during bootstrap if the interface is shut. * Updated the TODO file. * Some cosmetics patches. 2002-07-01 Alexandre Cassen * keepalived-0.6.5 released. * Fixed a NULL pointer exception while releasing IPVS entries. * Review the Makefile.in to fixe some conventional issue. Fixed a libipvs dependance code selection. * Christophe Varoqui, created the rpm spec file. * Roberto Nibali, helped during OLS with code cleanup. Review the whole code coding style to use more conventional indentation. The one used into LVS and Kernel code. Coding style provided by the following command : find . -name "*.[chS]" -exec indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl \ {} \; && find . -name "*~" -exec rm {} \; * Roberto Nibali and I review the DEBUG logging facility adding global DBG() func declaration. * Roberto Nibali fixed two potential buffer overflow (strcpy). * Richard L. Allbery, pointed out a fwmark issue. Healthcheckers is enabled if virtual service is a fwmark. * Some cosmetics patches. 2002-06-25 Alexandre Cassen * keepalived-0.6.4 released. * Rewrote the previous ip address utilities functions. Review the string to ulong convertion function to support CIDR filtering and more simple handling ("without all hexadecimal and shorthand"), pickted from Paul Vixie code. * VRRP : extended the notify framework to support scripts inside a vrrp_sync_group. view the sample/keepalived.conf.vrrp.sync file. * VRRP : Review the previous vrrp_sync_group block. New declaration is : view the sample/keepalived.conf.vrrp.sync file. * VRRP : fixed a FSM sync_group side effect in FAULT state. * Fixed a Kernel 2.2 code selection issue (ETHTOOL). * Added support to wensong libipvs. * Fixed a sorry_server cleanup side effect. * Alex Kramarov, fine the keepalived.init script to be compatible with redhat chkconfig. 2002-06-18 Alexandre Cassen * keepalived-0.6.3 released. * VRRP : Christian Motelet, pointed out a flapping issue when runing vrrp_sync_group on multiple NICs. This have been fixed adding leave FAULT state transition on both FSM state (read & read_to). The group leave fault state if all NIC of each VRRP Instance are functional. * Fixed some issue in the autoconf/automake scripts. 2002-06-16 Alexandre Cassen * keepalived-0.6.2 released. * Andres Salomon, enhanced the autoconf/automake scripts to be more generic and to facilitate cross compilation. Including more efficient IPVS code detection, Kernel version, install script location, ... * Johannes Erdfelt, fixed a genhash get request length calculation issue. * Johannes Erdfelt, fixed a wrong printed IP address issue due to a static pivot buffer called multiple times for a single syslog call. * Johannes Erdfelt, enhanced SMTP notification framework to use more compliant SMTP protocol handling. Enhanced both sending and receiving functions. A nice response code buffer handling calculating remote SMTP server retcode. * Johannes Erdfelt, fixed a NULL pointer exception into the 2.2 ipvswrapper code. * Aneesh Kumar, fixed a compilation issue for CI-LINUX checker compilation. * Jan Du Caju, fixed a compilation dependence selection into the VRRP framework when compiling without LVS support. This disable checkers activity update when compiled without LVS support. * fixed a dereferencing pointer into the parser. * move the dump configuration to printout conf after daemon initialization. * VRRP : Added support to start on complete init. VRRP framework and thus keepalived will start if VRRP instances are properly configured. 2002-06-13 Alexandre Cassen * keepalived-0.6.1 released. * Aneesh Kumar, and I added support to Cluster Infrastructure checkers. Providing HA-LVS for their cluster project (http://ci-linux.sourceforge.net/). The new checker added provide a derivation to the internal CI healthcheck mechanism. * Enhanced the Kernel netlink reflector to drive global healthcheckers activity. The policy implemented here is : If healthchecker is performing test on a service that belong to a VIP not owned by the director, then the healthchecker is suspended. This suspend/active state is particulary usefull if runing VRRP for HA => That way the backup LVS will not charge the realserver pool since LVS VIP is owned by master LVS. * Cosmetics patches into the vector lib. * VRRP : Rewrote the previous VRRP synchronization instance policy. Created a new config block called "vrrp_sync_group" that define VRRP instances synchronization dependences. That way we replace the previous "by-pair" sync approach by this "by-group" approach. This can be useefull for firewall HA with many NICs. Created a dedicated framework to speed up takeover synchronization. * VRRP : Added support to CIDR notation for VRRP VIPs definitions => VRRP VIPs definition like a.b.c.d/e. By default "e" value is set to 32. * VRRP : Added support to multicast source IP address selection => "mcast_src_ip" keyword. Can be usefull for strongly filtered env. The mcast group subscription is done using the NIC default IP after this mcast_src_ip is used if specified. * VRRP : Enhanced the link media failure detection. Added support to the new kernel SIOCETHTOOL probing for ETHTOOL_GLINK command. New drivers use this ETHTOOL interface to report link failure activity. During bootstrap a probe is done to determine the proper polling method to use for link media failure detection. The policy used is : probe for SIOCGMIIREG if not supported then try SIOCETHTOOL GLINK probe, otherwise use a ioctl SIOCGIFFLAGS polling function mirroring kernel NIC flags to localy reflected representation. * Ramon Kagan, and I updated the UserGuide.pdf. 2002-05-30 Alexandre Cassen * keepalived-0.5.9 released. * Added support to realserver_group. The work is not yet finished since it introduces new compilation design currently not supported. So please do not use yet. * VRRP : Review the script notification. Moved to a script per VRRP instance state => Created new keywords notify_backup|master|fault to run a specific script during backup|master|fault state transition. * VRRP : Added support to quoted strings for notify_backup|master|fault. Can now launch script passing arguments. See sample directory for examples. * VRRP : Added a protocol extension called "virtual_ipaddress_excluded". This configuration block is similar to "virtual_ipaddress" block => those VIPs (called E-VIPs) are set throught netlink kernel channel and gratuitous arp are sent over each E-VIP. The only difference is that they are not added into VRRP packet adverts. This can be usefull for big env where you want to run many VRRP VIPs (200 for example). VRRP packet lenght are limited to a 20 VIPs, if you want more VRRP VIPs add them to the "virtual_ipaddress_excluded" configuration block. * VRRP : Added more logging facility when setting/removings VIPs & E-VIPs. * VRRP : Created a new FSM state called become_master in charge of VIPs/E-VIPs/notifications handling. The goto_master state is now a state where the instance send an advert to force a new MASTER election setting the instance into a transition mode. If election success its finaly transit to become_master state to own VIPs/E-VIPs and launch scripts. * VRRP : Force a new MASTER election when receiving a lower prio advert. * VRRP : Review the vrrp_scheduler.c to use more conventional FSM design. This reduce and beautifull the code. * VRRP : Fixed a very noisy flapping issue observed on heavy loaded env. Simulating big traffic on a backbone figure out this flapping issue. Added support to a TIMER_MICRO_ADJUST to prevent against timer degradation. This can be view as a DOS protection policy. VRRP MASTER timers are adjusted if they are too degradated, due to heavy loaded networking env introducing latency receiving/sending VRRP protocol adverts. Thanks goes to Paul, for pointing it out and providing access to its Internet routing backbone. 2002-05-21 Alexandre Cassen * keepalived-0.5.8 released. * Added an OpenSSL Licence exception to grant Keepalived compilation with OpenSSL Toolkit. Thanks to Andres Salomon, for suggesting. * Added connection port selection for Healthcheckers (TCP_CHECK, HTTP|SSL_GET). Can be usefull for Healthcheck in fwmark LVS topology for grouping service. Thanks to Richard L. Allbery, for suggection. See samples directory for examples. * Fixed some IPVS exclusion code when running --disable-lvs. * Added support to VirtualHost selection when using HTTP|SSL_GET. See samples directory for examples. * Added VirtualHost selection into the genhash utility. * Fixed some IPVS sync daemon initializations issues. * Cometics patches in IPVS wrapper framework. * Added support to quoted string. This can be usefull if you are using MISC_CHECK and you want to pass arguments to called script. See samples. * Prepare work on real_server_group in order to group some realserver declaration. * VRRP : Fixed a password length exception causing an unwanted dropping issue. * VRRP : Enhanced the MASTER state to send gratuitous arp if receiving a remote lower prio advert => This fix a remote stalled ARP cache. Thanks to Simon Kirby, for discussing this case. 2002-05-02 Alexandre Cassen * keepalived-0.5.7 released. * Review autoconf/automake scripts to be more generic on system and code selection. Added primitives (configure) : --disable-lvs-syncd : Do not use IPVS sync daemon --disable-lvs : Do not use IPVS framework --disable-vrrp : Do not use VRRP framework --enable-debug : Compile with debugging flags * Fixed a SSL stream handling bug. Thanks to Andres Salomon, for pointing the issue. * Added a global memory counter to track global memory used. * Fixed configuration parser. read_line. Remove static allocated temporary read buffer. Only handle stream if line has been spitted into vector. * Limit maximum number of VIPs per VRRP Instance to 20. (for fragmentation, overhead, and others reasons). * Added IPVS wrapper support to persistence granularity. Thanks to Mike Zimmerman, for the suggestion. * Review smtp notifier to handle VRRP MASTER state transition alert. Thanks to Paul, for the suggestion. * Review the UserGuide.pdf to fixe some english issues :) Thanks to Jacques Thomas, for reviewing. 2002-04-13 Alexandre Cassen * keepalived-0.5.6 released. * VRRP : Review in "GOTO_MASTER_STATE" the IP address handling. send protocol adverts before registering IP address to the interface. * VRRP : Review the "LEAVE_MASTER_STATE" to only handle state transition if wanted states are BACKUP or FAULT. * VRRP : Review the BACKUP state to force new protocol election if receiving a lower priority advert. * VRRP : Fixed a BACKUP to MASTER state transition only if interface is reported UP. * VRRP : Fake the "ipvs_syncd_cmd" function if running LVS using a Kernel 2.2. 2002-04-10 Alexandre Cassen * keepalived-0.5.5 released. * Fixed a gratuitous ARP porting bug. * VRRP : Review the data structure to be more generic and clean with the rest of the code. * VRRP : Remove the interface flags (NIC) ioctl functions * VRRP : Created an interface (NIC) library giving access to common interface helpers functions. * VRRP : Created an interface lookup function creating a global interface structure during daemon bootstrap. Consist of a netlink RTM_GETLINK & RTM_GETADDR lookup, so we can work with a userspace interface representation. * VRRP : Create a netlink kernel reflection framework updating dynamically our interface structure according to kernel netlink broadcast. This design is highly inspired from zebra. => Reflection mean : wait for netlink kernel broadcast, if received, wakeup netlink filter to update our userspace representation. Prefer this design instead of a delayed netlink poller. That way we reduce global overhead. * VRRP : VRRP need to detect failure from many places. If netlink can notify for many troubles like mainly IFF_UP|DOWN & IFF_RUNNING, those flags are kernel drivers dependent. To reduce takeover time and performance we need to have informations like : Does the media link is present ?. The fact is that most of the new NICs own embended hardware chip providing such informations. So created a MII transceiver status register thread poller. Monitoring Basic Mode Status Register (BMSR) of the MII status words. Waiting for kernel NIC drivers hackers to support this functionnality through netlink (=> Like a IFF_RUNNING update broadcast). * VRRP : Linked the state machine to the global interface structure. NIC failure/events are handled. * VRRP : Review the whole state machine code to be more realistic. The State transition diagram described into the RFC2338 is an obtimist view. The VRRP state transition diagram implemented here is : +---------------+ +--------->| |<-------------+ | | Initialize | | | +------| |----------+ | | | +---------------+ | | | V V | +---------------+ +---------------+ | |---------------------->| | | Master | | Backup | | |<----------------------| | +---------------+ +---------------+ ^ | | | ^ | | | +---------------+ | | | | +------>| Dummy Master | | | | | +---------------+ | | | | | | | | | V | | | | +---------------+ | | | +------------>| |<----------+ | | | Fault | | +-----------------| |----------------+ +---------------+ * VRRP : Robust multicast handling. Something really strange is : after a NIC failure (in fallback mode) without closing the socket, multicast advert can be sent but not received ? really strange don t know why probably an IGMP resubmit ?. So multicast group is left during failover (media trouble, IFF_DOWN or !IFF_RUNNING). In fallback, we register a new membership and synchronize all the packet dispatcher fds. * VRRP : Fixed a checksum trouble using password authentication. * VRRP : Added support to the LVS sync daemon. This permit LVS sync daemon to be state drived by a specific VRRP instance. * Review the autoconf/automake to be more generic. * Some cosmetics patches. 2002-02-25 Alexandre Cassen * keepalived-0.5.3 released. * Added autoconf / automake generic scripts. * Rewrite the configuration file stream parser. Using a generic keywords tree. Each keyword refer a specific stream handler. The main stream processor is a multilevel recursive function getting file stream and backtracking the keyword tree. Kind of global compiler structure using event driven stream processing. * Re-design the global data structure to be much more generic and to dissociate LVS configuration related to checkers related. Remove static char lenght to use dynamic length strings. * Created a global timer framework. * Created a global vector template, used in cofiguration file parsing (both stream process & keywords tree generation). * Created a global list template, used in most of the code. * Review the global scheduler to remove repeated code. * Created a global checkers API. The design and goal here is to facilitate new checkers creation by localizing specific checker code into a single file without any other global framework integration. * Patched a SSL stream handling race condition finding end of stream. * Jan Holmberg, review MISC checker to use forked process to not degrade global scheduler timer. * Revisited the whole code to use new templates structures. * Fixed a url lentgh bug into the genhash utility. * Fabrice Bucher, fixed a timeout_persistence bug in the IPVS wrapper code. * Bradley McLean, added support to '0' port number service in VS manipulation. Useful for balancing all services (host rather than service). * Matthijs van der Klip, enhanced smtp framework to use SMTP header and email enclosed with angle brackets. 2001-12-20 Alexandre Cassen * keepalived-0.4.9a released. * Jan and I patched a memory pointer problems in vrrp_scheduler.c Thanks to Negrea Mihai, for reporting. * Jan Holmberg, patched a memory reallocation pointer exception in memory management framework. * Jan Holmberg, patched a vrrp vip set/remove retry. * Some cosmetics/logging patches. * Created Keepalived UserGuide. 2001-12-10 Alexandre Cassen * keepalived-0.4.9 released. * Jan Holmberg, added a memory managment framework. In debug mode it is used as a memory leak buster. We can so use it to debug quickly memory leaks (buffer overrun, allocation errors, ...). * Jan Holmberg and I added support to SSL. Checker SSL_GET. Can be used with autogenerated cert or with specific cafile, certfile, keyfile. * Use the OpenSSL, library for MD5 & SSL functions. * Jan Holmberg and I Rewrote the HTTP_GET code to use full asynchronous stream handling. The code use a common part for HTTP/SSL stream handling. Review the MD5 digest buffer computation, update MD5 over received buffer. * Patched some memory leaks in smtp handling. * Jan Holmbarg added support to LVS FWMARK. * Added command line option for keepalived. Used the libpopt library. -h, -v, -n, -d, -l, -f. * Jan Holmberg and I added debugging facility on keepalived console. * Added a BOOTSTRAP_DELAY of 1sec when registering checkers during daemon bootstrap. * VRRP : Jan Holmberg added possibility to run an extra script when VRRP Instance become or leave MASTER STATE (=> using a forked process). * Review/fine the whole code to apply cosmetics patch. * Rewrote the genhash utility. * Started checkers API specs. * doc doc doc... 2001-11-20 Alexandre Cassen * keepalived-0.4.8 released. * Rewrite the whole VRRP previous code. * VRRP : Created a hierarchic scheduling framework. Handle VRRP instances multiplexing on the same I/O fd. VRRP I/O events are handled by our global scheduling framework. Then the global sheduling framework call a VRRP I/O instance dispatcher to manage VRRP instances. * VRRP : Created a temporary socket pool to handle register our VRRP thread instances. We create & allocate a socket pool here. The soft design can be sum up by the following sketch : fd1 fd2 fd3 fd4 fdi fdi+1 -----\__/--------\__/---........---\__/--- | ETH0 | | ETH1 | | ETHn | +------+ +------+ +------+ Here we have n physical NIC. Each NIC own a maximum of 2 fds. (one for VRRP the other for IPSEC_AH). All our VRRP instances are multiplexed through this fds. So our design can handle 2*n multiplexing points. * VRRP : Review the multicast socket creating. We bind the socket to a specific NIC. inbound & outbound traffic are bound to the NIC. => why IP_ADD_MEMBERSHIP & IP_MULTICAST_IF doesnt set sk->bound_dev_if themself ??? !!! Needed for filter multicasted advert per interface. => For inbound binding we use SO_BINDTODEVICE kernel option. * VRRP : Created a read dispatcher thread to deal with our sockpool. Handle VRRP states & transition states. * VRRP : Created a VRRP synchronization instance circuit. This functionnality gave you the ability to monitor VRRP instance each other. This mean that if 2 VRRP instances are monitoring themself and if one of this instance change state, the other follow the same state. ex.: With 2 VRRP instances (VI_1 & VI_2) if VI_1 become backup then VI_2 become backup too. (symetricly with master VRRP state). * VRRP : Rewrite the netlink interface to use non blocking socket. * VRRP : Rewrite the ipaddress handling to use the new netlink interface. * VRRP : Remove the VRPP VMAC handling since linux kernel only permit to use one MAC address on a specific NIC. We use gratuitous arp when setting up VRRP VIP, to uptade remote host arp caches. => In certain case this can cause a TCP session renegociation which can cause a permature session end. => To be fully compliant with the VRRP RFC, need to patch the kernel to gave it the possibility to deal with more than one MAC address at a time. Give me clue on it please ! to same me a little time :) * Starting VRRP documentation. * Patch a pidfile handling bug when forking the keepalived daemon. Thanks goes to Gianni D'Aprile for pointing it to me. * Patch a timer race condition into the scheduling framework. This bug caused tcpcheck to respawn quickly... Thanks goes to Gianni D'Aprile for pointing it to me. 2001-11-04 Alexandre Cassen * keepalived-0.3.8 released. * Added support to native IPTABLE LVS CODE => using NAT on 2.4 kernel ipchains kernel support has been removed. * Added support to Direct Routing & Tunneling. * Review the keepalived.init script to be much more generic. 2001-09-14 Alexandre Cassen * keepalived-0.4.1 released. * Added support to LVS kernel 2.4 code 2001-08-23 Alexandre Cassen * keepalived-0.4.0 released. * Patch a race condition into the scheduler timer computation. * Patch a race condition into the tcp checker thread. Only register next timer thread if tcp connection is not in progress. * Patch a race condition into the http checker thread. Handle empty buffer returned from remote http server. * Patch a race condition into the dumping configuration process. A simple dereferencing pointer value...oops... * Eric Jarman, added MISC CHECKER. It Perform a system call to run an extra system or script. => security auditing needed for system call, buffer overflow over script path must be handled. * Added VRRP support using our scheduling I/O multiplexer. VRRP implementation support to IPSEC-AH using HMAC-96bits digest with anti-replay. rfc2402 & rfc2104. * Added routing table fetcher. We ignore route when it is a cloned route from other router, learn by an ICMP redirect or set by kernel. Only UNICAST route are stored. * Added dropping packet support. 2001-07-15 Alexandre Cassen * keepalived-0.3.5 released. * Rewrite the whole signal handling, registering a terminating thread on signal. * Move logsystem to syslog using facility LOG_INFO & LOG_DEBUG. * Added a daemonization function imported from zebra. * Rewrite the pidfile handling, check if daemon is running, if not remove eventual stalled pidfile and create new pidfile. * Added a strong scheduling framework based on an I/O multiplexer to handle asynchronous process. This code is imported from zebra and have been enhanced for keepalived purposes. Thread types are : . timeouted read on fd. . timeouted write on fd. . timer. . event. . terminate event. => The zebra framework have been enhanced to add support for timeouted read/write fds. => With this framework keepalived use a Boss/Worker thread model design, fetching ready thread from a master threading queues. * Rewrite the configuration file reader to add flexibility on extending. The dynamic data structure has been rewritten to use apropriate types. Right now parsing framework is ready for easy new checker structures integration. * Rewrite the smtp connector. The implementation take advantage of the I/O multiplexer. All read/write operations from/to the remote smtp server are done asynchronously. The implementation is rfc 821 compliant (multiple receiver are handled by a multiple RCPT TO command as specified in rfc821.3.1). * Rewrite the IPFW & IPVS wrappers. * Added support for NAT mask on IP MASQ rules (keyword nat_mask in configuration file). Added support for sorry server facility, so when all the server from a VS server pool are removed, a sorry server is automaticaly added to the VS pool (typically this is used when you have a spare server online). * Rewrite the previous checkers. Checkers are now based on a hierarchic layer stack framework. The protocol implemented for the moment is TCP. All layer 5 checkers are using layer4.c primitives with the same design : . a checker connector thread (creating the socket) registering the connection checker thread. . a connection checker thread testing connection states (error, in_progress, timeout, success). When connection success upper level thread are registered to handle checks. * Delay loop is now checkers specifics since we can use a multithreaded framework. * Update the PDF documentation file. keepalived-2.3.3/AUTHOR0000664000175000017500000000005111260075317010236 Alexandre Cassen, keepalived-2.3.3/Makefile.in0000664000175000017500000007643514772274255011416 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2001-2017 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = keepalived.spec Dockerfile CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(docdir)" DATA = $(doc_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir distdir-am dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Dockerfile.in $(srcdir)/Makefile.in \ $(srcdir)/keepalived.spec.in $(top_srcdir)/build-aux/ar-lib \ $(top_srcdir)/build-aux/compile \ $(top_srcdir)/build-aux/install-sh \ $(top_srcdir)/build-aux/missing COPYING ChangeLog INSTALL \ README TODO build-aux/ar-lib build-aux/compile \ build-aux/depcomp build-aux/install-sh build-aux/missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip # Exists only to be overridden by the user if desired. AM_DISTCHECK_DVI_TARGET = dvi distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ edit = echo " EDIT $@"; \ @SED@ -e "/^\[\!\[/d" SUBDIRS = lib keepalived doc bin_install EXTRA_DIST = AUTHOR CONTRIBUTORS snap README.md build_setup autogen.sh tools/timed_reload doc_DATA = README MOSTLYCLEANFILES = README ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} AM_DISTCHECK_CONFIGURE_FLAGS = \ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) all: all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): keepalived.spec: $(top_builddir)/config.status $(srcdir)/keepalived.spec.in cd $(top_builddir) && $(SHELL) ./config.status $@ Dockerfile: $(top_builddir)/config.status $(srcdir)/Dockerfile.in cd $(top_builddir) && $(SHELL) ./config.status $@ install-docDATA: $(doc_DATA) @$(NORMAL_INSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ done uninstall-docDATA: @$(NORMAL_UNINSTALL) @list='$(doc_DATA)'; test -n "$(docdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-zstd: distdir tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ *.tar.zst*) \ zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(docdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) @WITH_DBUS_FALSE@install-data-hook: clean: clean-recursive clean-am: clean-generic clean-local mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-local \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-docDATA @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-docDATA .MAKE: $(am__recursive_targets) install-am install-data-am \ install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ clean-local cscope cscopelist-am ctags ctags-am dist dist-all \ dist-bzip2 dist-gzip dist-hook dist-lzip dist-shar dist-tarZ \ dist-xz dist-zip dist-zstd distcheck distclean \ distclean-generic distclean-local distclean-tags \ distcleancheck distdir distuninstallcheck dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-data-hook install-docDATA install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am tags \ tags-am uninstall uninstall-am uninstall-docDATA .PRECIOUS: Makefile export DEBUG_CFLAGS export DEBUG_CPPFLAGS export DEBUG_LDFLAGS README: $(srcdir)/README.md @$(edit) '$(srcdir)/$@.md' >$@ @WITH_DBUS_TRUE@install-data-hook: @WITH_DBUS_TRUE@ @if [ @sysconfdir@ != /etc ]; then \ @WITH_DBUS_TRUE@ echo; \ @WITH_DBUS_TRUE@ echo "NOTE: To enable DBus to work you probably want to add:"; \ @WITH_DBUS_TRUE@ echo " @sysconfdir@/dbus1/system.d"; \ @WITH_DBUS_TRUE@ echo " to /etc/dbus-1/system-local.conf and restart dbus or reboot."; \ @WITH_DBUS_TRUE@ echo; \ @WITH_DBUS_TRUE@ echo " It should look something like:"; \ @WITH_DBUS_TRUE@ echo; \ @WITH_DBUS_TRUE@ echo ""; \ @WITH_DBUS_TRUE@ echo ""; \ @WITH_DBUS_TRUE@ echo ""; \ @WITH_DBUS_TRUE@ echo " @sysconfdir@/dbus-1/system.d"; \ @WITH_DBUS_TRUE@ echo ""; \ @WITH_DBUS_TRUE@ echo ""; \ @WITH_DBUS_TRUE@ echo; \ @WITH_DBUS_TRUE@ fi distclean-local: @rm -f aclocal.m4 keepalived-$(VERSION).tar.gz config.log config.status @rm -rf autom4te.cache # If we are in a git tree, set the last modified date of each unmodified source file # based on its git commit date. This will help repeatable builds. dist-hook: @if [ -x `type -p git` ]; then \ git rev-parse --is-inside-work-tree >/dev/null 2>&1; \ if [ $$? -eq 0 ]; then \ echo " DATESET git tree"; \ for f in `git ls-tree --full-tree -r --name-only HEAD`; \ do \ if [ -n "`git status --porcelain $$f`" ]; then continue; fi; \ if [ ! -f $(top_distdir)/$$f ]; then continue; fi; \ touch --date=@`git log -n 1 --format=%ct -- $$f` $(top_distdir)/$$f; \ done; \ cd lib; \ make git-commit.h; \ cd ..; \ fi \ fi @rm -f $(distdir)/README .PHONY: docker docker: dist docker build -t keepalived --build-arg GIT_VER=`grep GIT_COMMIT lib/git-commit.h | sed -e 's/.*"v[^-]*\(.*\)"/\1/'` . # clean all files that are generated by automake/autoconf etc autoclean: @$(MAKE) distclean @rm -f configure `find . -name Makefile.in` lib/config.h.in lib/git-commit.h lib/stamp-h[12] m4/pkg.m4 @rm -rf build-aux git-clean: @$(MAKE) autoclean clean-local: clean-local-snap # clean files that are generated by snapcraft .PHONY: clean-local-snap clean-local-snap: -rm -rf parts/ prime/ stage/ keepalived_*.snap # Added targets to maintain compatibility with keepalived releases 1.2.22 and earlier .PHONY: tarball rpm debug profile mrproper tarball: dist @RPM_TRUE@rpm: @RPM_TRUE@ @$(MAKE) dist @RPM_TRUE@ @cp -p keepalived-$(VERSION).tar.gz `rpm --eval "%{_sourcedir}"` @RPM_TRUE@@RPM_BIP_TRUE@ rpmbuild -ba --build-in-place keepalived.spec @RPM_TRUE@@RPM_BIP_FALSE@ rpmbuild -ba keepalived.spec debug: @$(MAKE) DEBUG_LDFLAGS=-ggdb profile: @$(MAKE) DEBUG_CFLAGS=-pg mrproper: @echo Please use `make distclean` # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/doc/0000775000175000017500000000000014772274313010152 5keepalived-2.3.3/doc/man/0000775000175000017500000000000014772274313010725 5keepalived-2.3.3/doc/man/man1/0000775000175000017500000000000014772274313011561 5keepalived-2.3.3/doc/man/man1/Makefile.am0000664000175000017500000000101414227040314013514 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2020-2020 Alexandre Cassen, DATE=`date -u --date=@\"\`git log -n 1 --format=%ct -- $@.in 2>/dev/null || date --reference=$(srcdir)/$@.in +%s\`\" +\"%Y-%m-%d\"` edit = echo " EDIT $@"; \ @SED@ \ -e "s|@DATE[@]|$(DATE)|g" dist_man1_MANS = genhash.1 genhash.1: Makefile.am $(top_builddir)/lib/config.h @$(edit) '$(srcdir)/$@.in' >$@ genhash.1: $(srcdir)/genhash.1.in EXTRA_DIST = genhash.1.in MOSTLYCLEANFILES = genhash.1 keepalived-2.3.3/doc/man/man1/genhash.1.in0000664000175000017500000000334414070603273013601 .\" .\" genhash(1) .\" .\" Copyright (C) 2010-2021 Alexandre Cassen, .TH genhash 1 "@DATE@" .SH NAME genhash \- md5 hash generation tool for remote web pages .SH SYNOPSIS .B "keepalived --genhash [options] [-s server-address] [-p port] [-u url]" .SH DESCRIPTION .B genhash is a tool used for generating md5sum hashes of remote web pages. .B genhash can use HTTP or HTTPS to connect to the web page. The output by this utility includes the HTTP header, page data, and the md5sum of the data. This md5sum can then be used within the .B keepalived(8) program, for monitoring HTTP and HTTPS services. .SH OPTIONS .TP .B --use-ssl, -S Use SSL to connect to the server. .TP .B --server , -s Specify the ip address to connect to. .TP .B --port , -p Specify the port to connect to. .TP .B --url , -u Specify the path to the file you want to generate the hash of. .TP .B --use-virtualhost , -V Specify the virtual host to send along with the HTTP headers. .TP .B --protocol , -P Specify the HTTP protocol version to use. protocol_version can be 1.0, 1.1 or 1.0c. 1.0c means protocol version 1.0 but with a "Connection: close" line; this is included in version 1.1 by default. .TP .B --timeout , -t Specify the connection timeout in seconds. .TP .B --fwmark , -m Set the specified firewall mark on the socket .TP .B --verbose, -v Be verbose with the output. .TP .B --help, -h Display the program help screen and exit. .TP .BR .SH SEE ALSO .BR keepalived (8), .BR keepalived.conf (5) .SH AUTHOR .br .B genhash was written by Alexandre Cassen . This man page was contributed by Andres Salomon for the Debian GNU/Linux system (but may be used by others). keepalived-2.3.3/doc/man/man1/Makefile.in0000664000175000017500000003736414772274255013570 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2020-2020 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = doc/man/man1 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man1dir = $(mandir)/man1 am__installdirs = "$(DESTDIR)$(man1dir)" NROFF = nroff MANS = $(dist_man1_MANS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(dist_man1_MANS) $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ DATE = `date -u --date=@\"\`git log -n 1 --format=%ct -- $@.in 2>/dev/null || date --reference=$(srcdir)/$@.in +%s\`\" +\"%Y-%m-%d\"` edit = echo " EDIT $@"; \ @SED@ \ -e "s|@DATE[@]|$(DATE)|g" dist_man1_MANS = genhash.1 EXTRA_DIST = genhash.1.in MOSTLYCLEANFILES = genhash.1 all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/man/man1/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/man/man1/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-man1: $(dist_man1_MANS) @$(NORMAL_INSTALL) @list1='$(dist_man1_MANS)'; \ list2=''; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list='$(dist_man1_MANS)'; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(MANS) installdirs: for dir in "$(DESTDIR)$(man1dir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-man uninstall-man: uninstall-man1 .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic cscopelist-am \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-man1 install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags-am uninstall uninstall-am uninstall-man \ uninstall-man1 .PRECIOUS: Makefile genhash.1: Makefile.am $(top_builddir)/lib/config.h @$(edit) '$(srcdir)/$@.in' >$@ genhash.1: $(srcdir)/genhash.1.in # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/doc/man/man1/genhash.10000664000175000017500000000335014772274313013201 .\" .\" genhash(1) .\" .\" Copyright (C) 2010-2021 Alexandre Cassen, .TH genhash 1 "2021-07-05" .SH NAME genhash \- md5 hash generation tool for remote web pages .SH SYNOPSIS .B "keepalived --genhash [options] [-s server-address] [-p port] [-u url]" .SH DESCRIPTION .B genhash is a tool used for generating md5sum hashes of remote web pages. .B genhash can use HTTP or HTTPS to connect to the web page. The output by this utility includes the HTTP header, page data, and the md5sum of the data. This md5sum can then be used within the .B keepalived(8) program, for monitoring HTTP and HTTPS services. .SH OPTIONS .TP .B --use-ssl, -S Use SSL to connect to the server. .TP .B --server , -s Specify the ip address to connect to. .TP .B --port , -p Specify the port to connect to. .TP .B --url , -u Specify the path to the file you want to generate the hash of. .TP .B --use-virtualhost , -V Specify the virtual host to send along with the HTTP headers. .TP .B --protocol , -P Specify the HTTP protocol version to use. protocol_version can be 1.0, 1.1 or 1.0c. 1.0c means protocol version 1.0 but with a "Connection: close" line; this is included in version 1.1 by default. .TP .B --timeout , -t Specify the connection timeout in seconds. .TP .B --fwmark , -m Set the specified firewall mark on the socket .TP .B --verbose, -v Be verbose with the output. .TP .B --help, -h Display the program help screen and exit. .TP .BR .SH SEE ALSO .BR keepalived (8), .BR keepalived.conf (5) .SH AUTHOR .br .B genhash was written by Alexandre Cassen . This man page was contributed by Andres Salomon for the Debian GNU/Linux system (but may be used by others). keepalived-2.3.3/doc/man/man5/0000775000175000017500000000000014772274313011565 5keepalived-2.3.3/doc/man/man5/Makefile.am0000664000175000017500000000126314227040314013526 # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2020-2020 Alexandre Cassen, DATE=`date -u --date=@\"\`git log -n 1 --format=%ct -- $@.in 2>/dev/null || date --reference=$(srcdir)/$@.in +%s\`\" +\"%Y-%m-%d\"` edit = echo " EDIT $@"; \ @SED@ \ -e "s|@DATE[@]|$(DATE)|g" \ -e 's|@RUN_DIR_ROOT[@]|$(RUN_DIR_ROOT)|g' \ -e 's|@KA_TMP_DIR[@]|$(KA_TMP_DIR)|g' dist_man5_MANS = keepalived.conf.5 keepalived.conf.5: $(builddir)/Makefile $(top_builddir)/lib/config.h $(srcdir)/Makefile.am @$(edit) '$(srcdir)/$@.in' >$@ keepalived.conf.5: $(srcdir)/keepalived.conf.5.in EXTRA_DIST = keepalived.conf.5.in MOSTLYCLEANFILES = keepalived.conf.5 keepalived-2.3.3/doc/man/man5/Makefile.in0000664000175000017500000003763314772274255013573 # Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am # # Keepalived OpenSource project. # # Copyright (C) 2020-2020 Alexandre Cassen, VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : subdir = doc/man/man5 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/lib/config.h \ $(top_builddir)/lib/config_warnings.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man5dir = $(mandir)/man5 am__installdirs = "$(DESTDIR)$(man5dir)" NROFF = nroff MANS = $(dist_man5_MANS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(dist_man5_MANS) $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ARFLAGS = @ARFLAGS@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ CSCOPE = @CSCOPE@ CTAGS = @CTAGS@ CYGPATH_W = @CYGPATH_W@ DBUS_DATADIR = @DBUS_DATADIR@ DEFAULT_CONFIG_DIR = @DEFAULT_CONFIG_DIR@ DEFAULT_CONFIG_FILE = @DEFAULT_CONFIG_FILE@ DEFAULT_CONFIG_FILENAME = @DEFAULT_CONFIG_FILENAME@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ EXPANDED_DATADIR = @EXPANDED_DATADIR@ GREP = @GREP@ HAVE_RPM = @HAVE_RPM@ HAVE_RPMBUILD = @HAVE_RPMBUILD@ HAVE_SPHINX_BUILD = @HAVE_SPHINX_BUILD@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ KA_CFLAGS = @KA_CFLAGS@ KA_CPPFLAGS = @KA_CPPFLAGS@ KA_LDFLAGS = @KA_LDFLAGS@ KA_LIBS = @KA_LIBS@ KA_TMP_DIR = @KA_TMP_DIR@ KEEPALIVED_CONFIG_OPTIONS = @KEEPALIVED_CONFIG_OPTIONS@ KEEPALIVED_RUNTIME_OPTIONS = @KEEPALIVED_RUNTIME_OPTIONS@ LDD = @LDD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINTAINERCLEANFILES = @MAINTAINERCLEANFILES@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ NETSNMP_CONFIG = @NETSNMP_CONFIG@ OBJEXT = @OBJEXT@ OLD_DEFAULT_CONFIG_FILE = @OLD_DEFAULT_CONFIG_FILE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ RUNSTATEDIR = @RUNSTATEDIR@ SAMPLES_DIR = @SAMPLES_DIR@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SNMP_SERVICE = @SNMP_SERVICE@ SPHINXBUILDNAME = @SPHINXBUILDNAME@ STRIP = @STRIP@ SYSTEMD_EXEC_START_OPTIONS = @SYSTEMD_EXEC_START_OPTIONS@ SYSTEMD_SERVICE_TYPE = @SYSTEMD_SERVICE_TYPE@ USE_LLD = @USE_LLD@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ systemdsystemunitdir = @systemdsystemunitdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ DATE = `date -u --date=@\"\`git log -n 1 --format=%ct -- $@.in 2>/dev/null || date --reference=$(srcdir)/$@.in +%s\`\" +\"%Y-%m-%d\"` edit = echo " EDIT $@"; \ @SED@ \ -e "s|@DATE[@]|$(DATE)|g" \ -e 's|@RUN_DIR_ROOT[@]|$(RUN_DIR_ROOT)|g' \ -e 's|@KA_TMP_DIR[@]|$(KA_TMP_DIR)|g' dist_man5_MANS = keepalived.conf.5 EXTRA_DIST = keepalived.conf.5.in MOSTLYCLEANFILES = keepalived.conf.5 all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/man/man5/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/man/man5/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-man5: $(dist_man5_MANS) @$(NORMAL_INSTALL) @list1='$(dist_man5_MANS)'; \ list2=''; \ test -n "$(man5dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.5[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \ done; } uninstall-man5: @$(NORMAL_UNINSTALL) @list='$(dist_man5_MANS)'; test -n "$(man5dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(MANS) installdirs: for dir in "$(DESTDIR)$(man5dir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man5 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-man uninstall-man: uninstall-man5 .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic cscopelist-am \ ctags-am distclean distclean-generic distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-man5 install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am tags-am uninstall uninstall-am uninstall-man \ uninstall-man5 .PRECIOUS: Makefile keepalived.conf.5: $(builddir)/Makefile $(top_builddir)/lib/config.h $(srcdir)/Makefile.am @$(edit) '$(srcdir)/$@.in' >$@ keepalived.conf.5: $(srcdir)/keepalived.conf.5.in # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: keepalived-2.3.3/doc/man/man5/keepalived.conf.5.in0000664000175000017500000041244514772020167015243 .TH keepalived.conf 5 @DATE@ "Keepalived" "Keepalived Configuration's Manual" .SH NAME keepalived.conf - configuration file for Keepalived .br .PP .SH Note: This documentation MUST be considered as THE exhaustive source of information in order to configure Keepalived. This documenation is supported and maintained by Keepalived Core-Team. .PP .SH DESCRIPTION \fBkeepalived.conf\fR is the configuration file which describes all the Keepalived keywords. Keywords are placed in hierarchies of blocks and subblocks, each layer being delimited by '{' and '}' pairs. .PP Comments start with '#' or '!' to the end of the line and can start anywhere in a line. .PP The keyword 'include' and variants allow inclusion of other configuration files from within the main configuration file, or from subsequently included files. .PP The format of the include directive is: \fBinclude\fR FILENAME .PP FILENAME can be a fully qualified or relative pathname, and can include wildcards, including csh style brace expressions such as "{foo/{,cat,dog},bar}" if glob() supports them. .PP After opening an included file, the current directory is set to the directory of the file itself, so any relative paths included from a file are relative to the directory of the including file itself. The include variants add additional include checks to the current include_check level (see below) The variants are: .br \fBincluder\fR FILENAME - same as include_check readable .br \fBincludem\fR FILENAME - same as include_check match .br \fBincludew\fR FILENAME - same as include_check wildcard_match .br \fBincludeb\fR FILENAME - same as include_check brace_match .br \fBincludea\fR FILENAME - all include_check checks .PP NOTE: If the libc glob() function does not support GLOB_ALTDIRFUNC (e.g. Musl libc as on Alpine Linux etc.), then only includea, includer and includew of the above options will work. .PP Why do we want to allow errors? Suppose a configuration has optional files in /etc/keepalived/conf.d, then \fIinclude_/etc/keepalived/conf.d/*\fR could be specified, but it should not error if there are no files in the directory; in this case \fIincluder\fR should be used. Otherwise it is sensible to use \fIincludea\fR. include handling will not work with if the include line uses conditional configuration or parameter substitution, since the detection of the include keywords is done before the processing on conditional configuration and parameter substitution. The basic \fIinclude\fR keyword is retained for backward compatibility, since it does not produce config errors if files could not be opened etc. .PP .SH PARAMETER SYNTAX \fB\fR is one of on|off|true|false|yes|no .br \fB\fR is a time value in seconds, including fractional seconds, e.g. 2.71828 or 3; resolution of timer is micro-seconds. .SH SCRIPTS There are three classes of scripts can be configured to be executed. (a) Notify scripts that are run when a vrrp instance or vrrp group changes state, or a virtual server quorum changes between up and down. (b) vrrp tracking scripts that will cause vrrp instances to go down if they exit a non-zero exist status, or if a weight is specified will add or subtract the weight to/from the priority of that vrrp instance. (c) LVS checker misc scripts that will cause a real server to be configured down if they exit with a non-zero status. By default the scripts will be executed by user keepalived_script if that user exists, or if not by root, but for each script the user/group under which it is to be executed can be specified. There are significant security implications if scripts are executed with root privileges, especially if the scripts themselves are modifiable or replaceable by a non root user. Consequently, security checks are made at startup to ensure that if a script is executed by root, then it cannot be modified or replaced by a non root user. All scripts should be written so that they will terminate on receipt of a SIGTERM signal. Scripts will be sent SIGTERM if their parent terminates, or it is a script that keepalived is awaiting its exit status and it has run for too long. .PP .SH Quoted strings Quoted strings are specified between " or ' characters and strings are delimited by whitespace. In the examples below the \' characters are not part of the strings and should not be specified: .nf .RS \'abcd" efg h jkl "mnop\' .RE .fi will be the single string: .nf .RS \'abcd efg h jkl mnop\' .RE whereas: .nf .RS \'abcd "efg h jkl" mnop\' .RE .fi will be the three strings: .nf .RS \'abcd\', \'efg h jkl\' and \'mnop\' .RE i.e. the " and ' characters are removed and any intervening whitespace is retained. .PP Quoted strings can also have escaped characters, like the shell. \\a, \\b, \\E, \\f, \\n, \\r, \\t, \\v, \\nnn and \\xXX (where nnn is up to 3 octal digits, and XX is up to two hex digits) and \\cC (which produces the control version of character C) are all supported. \\C for any other character C is just treated as an escaped version of character C, so \\\\ is a \\ character and \\" will be a " character, but it won't start or terminate a quoted string. .PP For specifying scripts with parameters, unquoted spaces will separate the parameters. If it is required for a parameter to contain a space, it should be enclosed in single quotes ('). For example .nf .RS $SG_NAME=SG1 $INST=low $USER=user notify_master "/etc/keepalived/notify_event.sh ' spaces\\\\x20f\\x69le ' '\\"s p a c e \\"' ${SG_NAME}.$INST master" $USER group .RE .fi specifies a notify_master script /etc/keepalived/notify_event.sh that will be executed as user:group with parameters .nf .RS \' spaces\\x20file \', \'"s p a c e "\', \'SG1.low\' and \'master\' .RE .PP .SH CONFIGURATION PARSER Traditionally the configuration file parser has not been one of the strengths of keepalived. Lot of efforts have been put to correct this even if this is not the primal goal of the project. .SH TOP HIERACHY .PP Keepalived configuration file is articulated around a set of configuration blocks. Each block is focusing and targetting a specific daemon family feature. These features are: .PP \fBGLOBAL CONFIGURATION\fR .PP \fBBFD CONFIGURATION\fR .PP \fBVRRPD CONFIGURATION\fR .PP \fBLVS CONFIGURATION\fR .SH GLOBAL CONFIGURATION contains subblocks of \fBGlobal definitions, Linkbeat interfaces, Interface up/down transition delays, Static track groups, Static addresses, Static routes,\fR and \fBStatic rules\fR .PP .SH Global definitions .PP .nf # Following are global daemon facilities for running # keepalived in a separate network namespace: # -- # Set the network namespace to run in. # The directory @RUN_DIR_ROOT@/run/keepalived will be created as an # unshared mount point, for example for pid files. # syslog entries will have _NAME appended to the ident. # Note: the namespace cannot be changed on a configuration reload. \fBnet_namespace \fRNAME # Add the IPVS configuration in the specified net namespace. It allows to easily # split the VIP traffic on a given namespace and keep the healthchecks traffic # in another namespace. If NAME is not specified, then the default namespace # will be used. \fBnet_namespace_ipvs \fRNAME # ipsets wasn't network namespace aware until Linux 3.13, and so # if running with an earlier version of the kernel, by default # use of ipsets is disabled if using a namespace and vrrp_ipsets # has not been specified. This options overrides the default and # allows ipsets to be used with a namespace on kernels prior to 3.13. \fBnamespace_with_ipsets\fR # If multiple instances of keepalived are run in the same namespace, # this will create pid files with NAME as part of the file names, # in @RUN_DIR_ROOT@/run/keepalived. # Note: the instance name cannot be changed on a configuration reload \fBinstance \fRNAME # Create pid files in @RUN_DIR_ROOT@/run/keepalived \fBuse_pid_dir\fR # Poll to detect media link failure using ETHTOOL, MII or ioctl interface # otherwise uses netlink interface. \fBlinkbeat_use_polling\fR # Time for main process to allow for child processes to exit on termination # in seconds. This can be needed for very large configurations. # (default: 5) \fBchild_wait_time \fRSECS Note: All processes/scripts run by keepalived are run with parent death signal set to SIGTERM. All such processes/scripts should either not change the action for SIGTERM, or ensure that the process/script terminates once SIGTERM is received, possibly following any cleanup actions needed. # Global definitions configuration block \fBglobal_defs \fR{ # In order to ensure that all processes read exactly the same configuration, # while the config is first read it is written, by default, to a memory based # file (or to an anonymous file in @KA_TMP_DIR@/ if memfd_create() is not supported). # If your configuration is very large, you may not want the copy to be # held in memory, in which case specifing the \fItmp_config_directory\fR causes the # configuration to be written to an anonymous file on the filesystem on which # the specified directory resides, which must be writeable by keepalived. # This setting cannot be changed on a reload, and it should be specified as # early as possible in the configuration. \fBtmp_config_directory\fR DIRECTORY # config_save_dir causes keepalived to save configuration state and # configuration files before and after each reload. This is used for debugging # purposes if there appear to be problems related to repeated reloads. # The directory will be created if it does not exist, but all parent # directories must exist. \fBconfig_save_dir\fR DIRECTORY # Set the process names of the keepalived processes to the default values: # keepalived, keepalived_vrrp, keepalived_ipvs, keepalived_bfd \fBprocess_names\fR # Specify the individual process names \fBprocess_name\fR NAME \fBvrrp_process_name\fR NAME \fBchecker_process_name\fR NAME \fBbfd_process_name\fR NAME # keepalived by default resolves script path names to remove symlinks. # To keep symlinks in pathnames, specify use_syslink_paths. \fBuse_symlink_paths \fR[] # The startup and shutdown scripts are run once, when keepalived starts # before any child processes are run, and when keepalived stops after # all child processes have terminated, respectively. # The original motivation for adding this feature was that although # keepalived can setup IPVS configuration using firewall marks, there # was no mechanism for adding configuration to set the firewall marks # (or for removing it afterwards). # This feature can also be used to setup the iptables framework required # if using iptables (see vrrp_iptables option below), modify interface # settings, or anything else that can be done from a script or program. # Only one startup script and one shutdown script can be specified. # The timeouts (in seconds default 10 seconds) are the time allowed for # scripts to run; if the timeout expires the scripts will be killed (this # is to stop keepalived hanging waiting for the scripts to terminate). \fBstartup_script\fR SCRIPT_NAME [username [groupname]] \fBstartup_script_timeout\fR SECONDS # range [1,1000] \fBshutdown_script\fR SCRIPT_NAME [username [groupname]] \fBshutdown_script_timeout\fR SECONDS # range [1,1000] # Set of email To: notify. To include a display name, the whole email address # must be included in double quotes("). \fBnotification_email \fR{ admin@example1.com "My admin " ... } # email from address that will be in the header (see comment above for # including a display name). # (default: keepalived@local_host_name) \fBnotification_email_from \fRadmin@example.com # Remote SMTP server used to send notification email. # IP address or domain name with optional port number. # (default port number: 25) \fBsmtp_server \fR127.0.0.1 [] # Name to use in HELO messages. # (default: local host name) \fBsmtp_helo_name \fR # SMTP server connection timeout in seconds. \fBsmtp_connect_timeout \fR30 # Sets default state for all smtp_alerts \fBsmtp_alert \fR # Sets default state for vrrp smtp_alerts \fBsmtp_alert_vrrp \fR # Sets default state for checker smtp_alerts \fBsmtp_alert_checker \fR # Logs every failed real server check in syslog # (nevertheless, SMTP alert is only sent when all retry checks failed # and real server transitions to DOWN state) \fBchecker_log_all_failures \fR # Don't send smtp alerts for fault conditions \fBno_email_faults\fR # String identifying the machine (doesn't have to be hostname). # (default: local host name) \fBrouter_id \fR # Multicast Group to use for IPv4 VRRP adverts # Defaults to the RFC5798 IANA assigned VRRP multicast address 224.0.0.18 # which You typically do not want to change. \fBvrrp_mcast_group4 \fR224.0.0.18 # Multicast Group to use for IPv6 VRRP adverts # (default: ff02::12) \fBvrrp_mcast_group6 \fRff02::12 # sets the default interface for static addresses. # (default: eth0) \fBdefault_interface \fRp33p1.3 # The sync daemon as provided by the IPVS kernel code only supports # one master and one backup daemon instance at a time to synchronize # the IPVS connection table. # See ipvsadm(8) man page for more details of the sync daemon. # Parameters are binding interface, and optional: # inst VRRP_INSTANCE (inst can be omitted for backward compatibility) # syncid (0 to 255) for lvs syncd, default is the VRID of vrrp instance, # or 0 if no vrrp instance # maxlen (1..65507) maximum packet length (limit is mtu - 20 - 8) # port (1..65535) UDP port number to use, default 8848 # ttl (1..255) # group - multicast group address(IPv4 or IPv6), default 224.0.0.81 # If VRRP_INSTANCE is not specified, both the master and backup sync daemons # will be run as long as keepalived is running, otherwise the sync daemon # master/backup state tracks the state of the specified vrrp instance: if # the vrrp instance is in master state, only the master sync daemon will run, # if the vrrp instance is not master, only the backup sync daemon will run. # NOTE: maxlen, port, ttl and group are only available on Linux 4.3 or later. # See kernel source doc/Documentation/networking/ipvs-sysctl.txt for details of # parameters controlling IPVS and the sync daemon. # /proc/net/ip_vs* provide some details about the state of IPVS. \fBlvs_sync_daemon \fR [[inst] ] [id ] \e [maxlen ] [port ] [ttl ] [group ] # lvs_timeouts specifies the tcp, tcp_fin and udp connection tracking timeouts # in seconds. At least one value must be specified; not setting a value leaves # it unchanged from when keepalived started. \fBlvs_timeouts\fR [tcp SECS] [tcpfin SECS] [udp SECS] # flush any existing LVS configuration at startup \fBlvs_flush\fR # flush remaining LVS configuration at shutdown (for large configurations # this is much faster than the default approach of deleting each RS and # each VS individually). # If VS is specified, remove each keepalived managed virtual # server without explicitly removing the real servers (the kernel will # remove them). \fBlvs_flush_on_stop [VS]\fR # number of gratuitous ARP messages to send at a time after # transition to MASTER. # (default: 5) \fBvrrp_garp_master_repeat \fR1 # delay for second set of gratuitous ARPs after transition to MASTER. # in seconds, 0 for no second set. # (default: 5) \fBvrrp_garp_master_delay \fR10 # number of gratuitous ARP messages to send at a time after # lower priority advert received when MASTER. # (default: vrrp_garp_master_repeat) \fBvrrp_garp_lower_prio_repeat \fR1 # delay for second set of gratuitous ARPs after lower priority # advert received when MASTER. # (default: vrrp_garp_master_delay) \fBvrrp_garp_lower_prio_delay \fR10 # minimum time interval for refreshing gratuitous ARPs while MASTER. # in seconds (resolution seconds). # (default: 0 (no refreshing)) \fBvrrp_garp_master_refresh \fR60 # number of gratuitous ARP messages to send at a time while MASTER # (default: 1) \fBvrrp_garp_master_refresh_repeat \fR2 # Delay between gratuitous ARP messages sent on an interface # decimal, seconds (resolution usecs). # (default: 0) \fBvrrp_garp_interval \fR0.001 # Delay between unsolicited NA messages sent on an interface # decimal, seconds (resolution usecs). # (default: 0) \fBvrrp_gna_interval \fR0.000001 # By default keepalived sends 5 gratuitions ARP/NA messages at a # time, and after transitioning to MASTER sends a second block of # 5 messages 5 seconds later. # With modern switches this is unnecessary, so setting vrrp_min_garp # causes only one ARP/NA message to be sent, with no repeat 5 seconds # later. \fBvrrp_min_garp \fR[] # The following option causes periodic GARP/NA messages to be sent on # interfaces of VIPs/eVIPs that are not the interface of the VRRP # instance, in order to ensure that switch MAC caches are maintained # (specified in seconds). # Many switches have a default cache timeout of 300 seconds, and so # a garp repeat rate of 1/3rd of that would be sensible. The maximum # permitted value is 1 day (86400 seconds); # By default, it will only send on VMAC interfaces; specifying \fBall\fR # will cause it to send GARP/NA on each interface used by the VRRP instance. \fBvrrp_garp_extra_if [all] \fR100 # Default value for vrrp down_timer_adverts. \fBvrrp_down_timer_adverts \fR[1:100] # If a lower priority advert is received, don't send another advert. # This causes adherence to the RFCs prior to RFC9568. # Defaults to false, unless strict_mode is set. \fBvrrp_lower_prio_no_advert \fR[] # If we are master and receive a higher priority advert, send an advert # (which will be lower priority than the other master), before we # transition to backup. This means that if the other master has # garp_lower_priority_repeat set, it will resend garp messages. # This is to get around the problem of there having been two simultaneous # masters, and the last GARP messages seen were from us. \fBvrrp_higher_prio_send_advert \fR[] # Set the default VRRP version to use # (default: 2, but IPv6 instances will use version 3) \fBvrrp_version \fR<2 or 3> # See vrrp_instance description of V3_checksum_as_V2 \fBv3_checksum_as_v2\fR [] # keepalived uses a firewall (either nftables or iptables) for two purposes: # i) To implement no_accept mode # ii) To stop IGMP/MLD/Router-Solicit packets being sent on VMAC interfaces, # and to move IGMP/MLD messages onto the underlying interface. # If both vrrp_iptables and vrrp_nftables are specified, keepalived will use # nftables and not iptables. Similarly, if the iptables command is generating # nftables configuration, or there is no iptables command installed, # keepalived will use nftables rather than iptables. # If neither vrrp_nftables or vrrp_iptables are specified but VMACs are in use # or no_accept is specified, keepalived will use nftables if it is available. # Use nftables as the firewall. # TABLENAME must not exist, and must be different for each # instance of keepalived running in the same network namespace. # Default tablename is keepalived, and priority is -1. # keepalived will create base chains in the table. # counters means counters are added to the rules (primarily for # debugging purposes). # ifindex means create IPv6 link local sets using ifindex rather # than ifnames. This is the default unless the vrrp_instance has # set dont_track_primary. The alternative is to use interface names # as part of the set key, but the nft utility prior to v0.8.3 will # then not output interface names properly. \fBnftables \fR[TABLENAME] \fBnftables_priority \fRPRIORITY \fBnftables_counters\fR \fBnftables_ifindex\fR # Similarly for IPVS iptables - used for setting fwmarks for virtual # server groups. keepalived will allocate a fwmark for each virtual # server group, so that only one virtual server for each group needs # to be configured in IPVS, by using a fwmark, and nftables will be # used to set the fwmark for each of the virtual server # address/protocol/port combinations specified. # nftables_ipvs_start_fwmark specifies the first fwmark for keepalived # to use (default 1000). This will be incremented for each subsequent # virtual server group. \fBnftables_ipvs \fR[TABLENAME] \fBnftables_ipvs_priority \fRPRIORITY \fBnftables_ipvs_start_fwmark \fRNUMBER # Use iptables as the firewall. # Note: it is necessary for the specified chain to exist in # the iptables and/or ip6tables configuration, and for the chain # to be called from an appropriate point in the iptables configuration. # It will probably be necessary to have this filtering after accepting # any ESTABLISHED,RELATED packets, because IPv4 might select the VIP as # the source address for outgoing connections. # Note: although the default chains that are used are INPUT and OUTPUT, # since those are the only chains that will always exist, it is not safe # or sensible to use those chains and specific chains should be created # and called from appropriate points in the iptables configuration. The # chains used for keepalived should not be used for any other purpose, and # should have no rules configured, other than the rules that keepalived # manages. # A startup_script (see above) can be used to create the chains and to # add rules to call them. A shutdown_script can be used to remove the # iptables configuration added by the startup_script. # Note2: If using ipsets, the iptables VIP rules are appended to the end # of the specified chains; if not using ipsets, the VIP rules are inserted # at the beginning of the chains. Any IGMP rules are always appended to # the end of the chains. # (default: INPUT) \fBvrrp_iptables \fRkeepalived # or for outbound filtering as well # Note, outbound filtering won't work with IPv4, since the VIP can be # selected as the source address for an outgoing connection. With IPv6 # this is unlikely since the addresses are deprecated. \fBvrrp_iptables \fRkeepalived_in keepalived_out # or to to use default chains (INPUT and OUTPUT) \fBvrrp_iptables\fR # Keepalived may have the option to use ipsets in conjunction with # iptables. If so, then the ipset names can be specified, defaults # as below. If no names are specified, ipsets will not be used, # otherwise any omitted names will be constructed by adding "_if" # and/or "6" and _igmp/_mld/_nd to previously specified names. \fBvrrp_ipsets \fR[keepalived [keepalived6 [keepalived_if6 [keepalived_igmp [keepalived_mld [keepalived_vmac_nd]]]]]] # An alternative to moving IGMP messages from VMACs to their parent interfaces # is to disable them altogether in the kernel by setting # igmp_link_local_mcast_reports false. # This stops IGMP join etc messages for 224.0.0.0/24, since they should # always be forwarded to all interfaces (see RFC4541). # This is available from Linux 4.3 onwards. disable_local_igmp # The following enables checking that when in unicast mode, the # source address of a VRRP packet is one of our unicast peers. \fBvrrp_check_unicast_src\fR # Checking all the addresses in a received VRRP advert can be time # consuming. Setting this flag means the check won't be carried out # if the advert is from the same master router as the previous advert # received. # (default: don't skip) \fBvrrp_skip_check_adv_addr\fR # Enforce strict VRRP protocol compliance. This currently includes # enforcing the following. Please note that other checks may be # added in the future if they are found to be missing: # 0 VIPs not allowed # unicast peers not allowed # IPv6 addresses not allowed in VRRP version 2 # First IPv6 VIP is link local # State MASTER can be configured if and only if priority is 255 # Authentication is not supported # Preempt delay is not supported # Accept mode cannot be set for VRRPv2 # If accept/no accept is not specified, accept is set if priority # is 255 aand cleared otherwise # Gratuitous ARP repeats cannot be enabled # Cannot clear lower_prio_no_advert # Cannot set higher_prio_send_advert # Cannot use vmac_xmit_base # Cannot have no VIPs with VRRPv3 # If priority is 255 ignore received adverts \fBvrrp_strict\fR # Send vrrp instance priority notifications on notify FIFOs. \fBvrrp_notify_priority_changes\fR # The following options can be used if vrrp, checker or bfd processes # are timing out. This can be seen by a backup vrrp instance becoming # master even when the master is still running, because the master or # backup system is too busy to process vrrp packets. # -- # keepalived can, if it detects that it is not running sufficiently # soon after a timer should expire, increase its priority, first # of all switching to realtime scheduling, and if that is not # sufficient, it will then increase its realtime priority by one each # time it detects a further delay in running. If the event that realtime # scheduling is enabled, RLIMIT_RTTIME will be set, using the values for # {bfd,checker,vrrp}_rlimit_rttime (see below). These values may need # to be increased for slower processors. # -- # To limit the maximum increased automatic priority, specify the following # (0 doesn't use automatic priority increases, and is the default. -1 disables # the warning message at startup). Omitting the priority sets the maximum value. \fBmax_auto_priority\fR [<-1 to 99>] # 99 is really sched_get_priority_max(SCHED_RR) # Minimum delay in microseconds after timer expires before keeplalived is # scheduled after which the process priority will be auto incremented # (default is 1000000 usecs (1 second), maximum is 10000000 (10 seconds)) \fBmin_auto_priority_delay\fR # Set the vrrp child process priority (Negative values increase priority) \fBvrrp_priority \fR<-20 to 19> # Set the checker child process priority \fBchecker_priority \fR<-20 to 19> # Set the BFD child process priority \fBbfd_priority \fR<-20 to 19> # Set the vrrp child process non swappable \fBvrrp_no_swap\fR # Set the checker child process non swappable \fBchecker_no_swap\fR # Set the BFD child process non swappable \fBbfd_no_swap\fR # The following options can be used to force vrrp, checker and bfd # processes to run on a restricted CPU set. # You can either bind processes to a single CPU or define a set of # cpu. In that last case Linux kernel will be restricted to that cpu # set during scheduling. Forcing process binding to single CPU can # increase performances on heavy loaded box. # INTEGER following configuration keyword are representing cpu_id # as shown in /proc/cpuinfo on line "processor:" # -- # Set CPU Affinity for the vrrp child process \fBvrrp_cpu_affinity\fR []...[] # Set CPU Affinity for the checker child process \fBchecker_cpu_affinity\fR []...[] # Set CPU Affinity for the bfd child process \fBbfd_cpu_affinity\fR []...[] # Set the vrrp child process to use real-time scheduling # at the specified priority \fBvrrp_rt_priority \fR<1..99> # Set the checker child process to use real-time scheduling # at the specified priority \fBchecker_rt_priority \fR<1..99> # Set the BFD child process to use real-time scheduling # at the specified priority \fBbfd_rt_priority \fR<1..99> # Set the limit on CPU time between blocking system calls, # in microseconds # (default: 10000) \fBvrrp_rlimit_rttime \fR>=2 \fBchecker_rlimit_rttime \fR>=2 \fBbfd_rlimit_rttime \fR>=2 # If Keepalived has been build with SNMP support, the following # keywords are available. # Note: Keepalived, checker and RFC support can be individually # enabled/disabled # -- # Specify socket to use for connecting to SNMP master agent # (see source module keepalived/vrrp/vrrp_snmp.c for more details) # (default: unix:/var/agentx/master) \fBsnmp_socket \fRudp:1.2.3.4:705 # enable SNMP handling of vrrp element of KEEPALIVED MIB \fBenable_snmp_vrrp\fR # enable SNMP handling of checker element of KEEPALIVED MIB \fBenable_snmp_checker\fR # enable SNMP handling of RFC2787 and RFC6527 VRRP MIBs \fBenable_snmp_rfc\fR # enable SNMP handling of RFC2787 VRRP MIB \fBenable_snmp_rfcv2\fR # enable SNMP handling of RFC6527 VRRP MIB \fBenable_snmp_rfcv3\fR # enable SNMP traps \fBenable_traps\fR # When SNMP requests are made, the checker process only updates the # virtual and real server stats from the kernel if the last time the # stats for that virtual server were read was more than this configured # interval (in seconds). The default interval is 5 seconds, and the # valid range is 0.001 (1 milli-second) to 30 seconds. \fBsnmp_vs_stats_update_interval\fR # Like snmp_vs_stats_update_interval but for real servers. Stats for # real servers are only read if there is an SNMP request for real server # stats. \fBsnmp_rs_stats_update_interval\fR # If Keepalived has been build with DBus support, the following # keywords are available. # -- # Enable the DBus interface \fBenable_dbus\fR # Name of DBus service # Useful if you want to run multiple keepalived processes with DBus enabled # (default: org.keepalived.Vrrp1) \fBdbus_service_name \fRSERVICE_NAME # String to use for DBus path when VRRP instance has no interface configured # Useful if your system has an interface named "none"! # (default: "none") \fBdbus_no_interface_name \fRNAME # Specify the default username/groupname to run scripts under. # If this option is not specified, the user defaults to keepalived_script # if that user exists, otherwise the uid/gid under which keepalived is running. # If groupname is not specified, it defaults to the user's group. \fBscript_user \fRusername [groupname] # Don't run scripts configured to be run as root if any part of the path # is writable by a non-root user. Also, enforce the default script_user is # keepalived_script, and don't default to the user under which keepalived # is running (usually root). \fBenable_script_security\fR # Rather than using notify scripts, specifying a fifo allows more # efficient processing of notify events, and guarantees that they # will be delivered in the correct sequence. # NOTE: the FIFO names must all be different # -- # FIFO to write notify events to # See vrrp_notify_fifo and lvs_notify_fifo for format of output # For further details, see the description under vrrp_sync_group. # see doc/samples/sample_notify_fifo.sh for sample usage. \fBnotify_fifo \fRFIFO_NAME [username [groupname]] # script to be run by keepalived to process notify events # The FIFO name will be passed to the script as the last parameter \fBnotify_fifo_script \fRSTRING|QUOTED-STRING [username [groupname]] # FIFO to write vrrp notify events to. # The string written will be a line of the form: INSTANCE "VI_1" MASTER 100 # and will be terminated with a new line character. # For further details of the output, see the description under vrrp_sync_group # and doc/samples/sample_notify_fifo.sh for sample usage. \fBvrrp_notify_fifo \fRFIFO_NAME [username [groupname]] # script to be run by keepalived to process vrrp notify events # The FIFO name will be passed to the script as the last parameter \fBvrrp_notify_fifo_script \fRSTRING|QUOTED-STRING [username [groupname]] # FIFO to write notify healthchecker events to # The string written will be a line of the form: # VS [192.168.201.15]:tcp:80 {UP|DOWN} # RS [1.2.3.4]:tcp:80 [192.168.201.15]:tcp:80 {UP|DOWN} # and will be terminated with a new line character. \fBlvs_notify_fifo \fRFIFO_NAME [username [groupname]] # script to be run by keepalived to process healthchecher notify events # The FIFO name will be passed to the script as the last parameter \fBlvs_notify_fifo_script \fRSTRING|QUOTED-STRING [username [groupname]] # By default, when keepalived reloads the vrrp instance and sync group states # are not written to the relevant FIFOs. Setting this option will cause the # states to be sent to the FIFO(s) when keepalived reloads. \fBfifo_write_vrrp_states_on_reload\fR # Allow configuration to include interfaces that don't exist at startup. # This allows keepalived to work with interfaces that may be deleted and restored # and also allows virtual and static routes and rules on VMAC interfaces. # allow_if_changes allows an interface to be deleted and recreated with a # different type or underlying interface, eg changing from vlan to macvlan # or changing a macvlan from eth1 to eth2. This is predominantly used for # reporting duplicate VRID errors at startup if allow_if_changes is not set. \fBdynamic_interfaces [allow_if_changes]\fR # The following options are only needed for large configurations, where either # keepalived creates a large number of interface, or the system has a large # number of interface. These options only need using if # "Netlink: Receive buffer overrun" messages are seen in the system logs. # If the buffer size needed exceeds the value in /proc/sys/net/core/rmem_max # the corresponding force option will need to be set. # -- # Set netlink receive buffer size. This is useful for # very large configurations where a large number of interfaces exist, and # the initial read of the interfaces on the system causes a netlink buffer # overrun. \fBvrrp_netlink_cmd_rcv_bufs \fRBYTES \fBvrrp_netlink_cmd_rcv_bufs_force \fR \fBvrrp_netlink_monitor_rcv_bufs \fRBYTES \fBvrrp_netlink_monitor_rcv_bufs_force \fR # The vrrp netlink command and monitor socket the checker command and # and monitor socket and process monitor buffer sizes can be independently set. # The force flag means to use SO_RCVBUFFORCE, so that the buffer size # can exceed /proc/sys/net/core/rmem_max. \fBlvs_netlink_cmd_rcv_bufs \fRBYTES \fBlvs_netlink_cmd_rcv_bufs_force \fR \fBlvs_netlink_monitor_rcv_bufs \fRBYTES \fBlvs_netlink_monitor_rcv_bufs_force \fR # As a guide for process_monitor_rcv_bufs for 1400 processes terminating # simultaneously, 212992 (the default on some systems) is insufficient, whereas # 500000 is sufficient. \fBprocess_monitor_rcv_bufs \fRBYTES \fBprocess_monitor_rcv_bufs_force \fR # When a socket is opened, the kernel configures the max rx buffer size for # the socket to /proc/sys/net/core/rmem_default. On some systems this can be # very large, and even generally this can be much larger than necessary. # This isn't a problem so long as keepalived is reading all queued data from # it's sockets, but if rmem_default was set sufficiently large, and if for # some reason keepalived stopped reading, it could consume all system memory. # The vrrp_rx_bufs_policy allows configuring of the rx bufs size when the # sockets are opened. If the policy is MTU, the rx buf size is configured # to the total of interface's MTU * vrrp_rx_bufs_multiplier for each vrrp # instance using the socket. Likewise, if the policy is ADVERT, then it is # the total of each vrrp instances advert packet size * multiplier. # (default: use system default) \fBvrrp_rx_bufs_policy \fR[MTU|ADVERT|NUMBER] # (default: 3) \fBvrrp_rx_bufs_multiplier \fRNUMBER # Send notifies at startup for real servers that are starting up \fBrs_init_notifies\fR # Don't send an email every time a real server checker changes state; # only send email when a real server is added or removed \fBno_checker_emails\fR # The umask to use for creating files. The number can be specified in hex, octal # or decimal. BITS are I{R|W|X}{USR|GRP|OTH}, e.g. IRGRP, separated by '|'s. # IRWX{U|G|O} can also be specified. # The default umask is IXUSR | IRWXG | IRWXO. This option cannot override the # command-line option. \fBumask \fR[NUMBER|BITS] # On some systems when bond interfaces are created, they can start passing traffic # and then have a several second gap when they stop passing traffic inbound. This # can mean that if keepalived is started at boot time, i.e. at the same time as # bond interfaces are being created, keepalived doesn't receive adverts and hence # can become master despite an instance with higher priority sending adverts. # This option specifies a delay in seconds before vrrp instances start up after # keepalived starts, \fBvrrp_startup_delay \fR5.5 # The following will cause logging of receipt of VRRP adverts for VRIDs not configured # on the interface on which they are received. \fBlog_unknown_vrids\fR # Specify the prefix for generated VMAC names (default "vrrp") \fBvmac_prefix \fRSTRING # Specify the prefix for generated VMAC names for VIPs which use a VMAC but are not # on the VRRP instance's interface (default vmac_prefix value) \fBvmac_addr_prefix \fRSTRING # Specify random seed for ${_RANDOM}, to make configurations repeatable (default # is to use a seed based on the time, so that each time a different configuration # will be generated). \fBrandom_seed \fRUNSIGNED_INT # If a configuration reload is attempted with an updated configuration file that has # errors, keepalived may terminate, and possibly enter a loop indefinitely restarting # and terminating. If reload_check_config is set, then keepalived will attempt to # validate the configuration before initiating a reload, and only initiate the reload # if the configuration is valid. \fBreload_check_config \fR[LOG_FILE] # Treat any missing include file as an error. The OPTIONS can be any combination of # readable - error if a match is not a readable file # match - error if no file matches (unless wildcard specified) # wildcard_match - error if no file matches (even if wildcard specified) # brace_match - error if a brace expansion does not match a file # Note: match, wildcard_match and brace_match include the readable check. # The setting of include_check is saved when a new include file is opened, and restored # when the file is closed. This means that the include_check setting when reading a # file cannot be changed by a subsequently included file. To change the setting for all # included files, include_check should be set at the beginning of the configuration file # specified in the command line (default /etc/keepalived/keepalived.conf). # Note2: If the libc glob() function does not support GLOB_ALTDIRFUNC (e.g. Musl libc as # on Alpine Linux etc.), then only readable and wildcard_match of the above options will work. # It is possible to add or remove individual settings; '+' means add the following # checks, '-' means remove the following checks. For example # \fIinclude_check +match -wildcard_match\fR # adds the requirement that there is a matching file, and removes the requirement for # wildcard matches. # If no option is specified, it is the same as specifying all options. \fBinclude_check \fR[OPTIONS] # reload_time_file allows a reload of keepalived to be scheduled in the future. This is # particularly useful if there is a master keepalived and one or more backup keepalived # instances and the new configuration is incompatible with the previous configuration, # e.g. adding or removing VIPs which would cause adverts to be rejected. # All the instances can be scheduled to reload at the same time, thereby ensuring that # no mismatching adverts are received by the backup instances. # The configuration specifies a file which keepalived will monitor. The first line of # the file must contain a valid time or date/time exactly in the formats specified below. # When keepalived starts up, it reads the file if it exists, and schedules a reload at # the specified time. If the file does not exist, then when it is subsequently created # a reload will be scheduled. If the file is updated, the reload time will be modified # accordingly. If the file is deleted, the reload is cancelled. # Normally when the reload occurs the specified file is deleted, since the reload has # been done; if the file included a date then the reload will be in the past and so # ignored. However, if there is no date, then if the file were reread following the # reload, a reload would be scheduled for 24 hours time. In order to stop this, the # file is deleted (unlinked) by default. If reload_repeat is specified, then the # file is not deleted, and if the file contains a time only with no date, then # keepalived will keep reloading at that time every day until the file is removed or # modified. # If the directory containing the file does not exist at startup/reload, or if the # directory is removed or renamed, then no future scheduled reloads will occur until # a manual (SIGHUP) reload is done or keepalived restarts. # The permitted formats of the entry in the timer file are precisely: # HH:MM:SS # YY-MM-DD HH:MM:SS # YYYY-MM-DD HH:MM:SS # each with an optional 'Z' at the end. # There must be no leading or trailing whitespace, and only one space between the date # and the time. # If there is a 'Z' at the end of the time, the time is parsed as UTC, otherwise the # time is the localtime for the environment in which keepalived is running. If the # systems which are being reloaded are in different timezones, it is probably safer to # use UTC. # If using local time with daylight savings, beware that some times don't exist and # some times are duplicated and hence ambiguous. \fBreload_time_file\fR ABSOLUTE-PATHNAME-OF-FILE \fBreload_repeat\fR # Some users frequently update their configurations and reload keepalived. reload_file # provides a mechanism that allows the configuration update processes not to update the # configuration files while keepalived is reading them. # The reload file will be created by keepalived before it starts reading configuration # files, unless the file exists. If the file already exists, it will be truncated. Once # keepalived has completed reading the files it will remove the reload file. # If reload_file with no file name is specified, the default filename keepalived.reload # in the PID directory will be used. # The best way to use the reload file is for the configuration update process to touch # the reload file before it signals keepalived to reload, and then wait for the file # to be deleted, which indicates that keepalived has finished reading the config files. # When keepalived starts reading the configuration files, since it truncates the reload # file, if update process creates the reload_file with non-zero size, it can detect # the reloading starting by the reload_file becoming zero length. \fBreload_file\fR [ABSOLUTE-PATHNAME-OF-FILE] # Sending SIGUSR1 to keepalived causes it to dump its data structures # for debugging purposes, although some users use this feature and # process the output. Please note that the format of the .data files # produced is not guaranteed to maintain backward compatibility. # The standard file names are keepalived_parent.data, keepalived.data, # keepalived_check.data and keepalived_bfd.data. This causes a problem # if more than one keepalived instance is running on a system. # In order to alleviate this, enabling data_use_instance includes the # instance name and network namespace in the file name of the .data files. # This also applies to SIGUSR2 for outputting stats. \fBdata_use_instance \fR[] # If the files produced by SIGUSR1 and SIGUSR2 are on slow storage, then writing to # them may cause keepalived processes to block. This option allows specifying the # location to which the files should be written, and should ideally be a tmpfs rather # than a real disc, and definitely not network attached storage or other storage that # can block for more than a few microseconds. # If path ends in a / it is considered to be a directory and the normal filenames # in that directory will be used, otherwise it will be used as the full path name # of the file to be written. Note that \fBdata_use_instance\fR may also modify the # file name. # For the state files, these are template names and "_checker" will be added # for the checker process, "_bfd" will be added for the BFD process and # "_parent" will be added for the parent process. \fBstate_file_location \fRpath \fBstats_file_location \fRpath \fBjson_file_location \fRpath # json_version 2 puts the VRRP data in a named array and adds # track_process details. Default is version 1. \fBjson_version \fR{1|2} } .fi .SH Linkbeat interfaces .PP The linkbeat_interfaces block allows specifying which interfaces should use polling via MII, Ethtool or ioctl status rather than rely on netlink status updates. This allows more granular control of global definition \fBlinkbeat_use_polling\fR. .PP This option is preferred over the deprecated use of \fBlinkbeat_use_polling\fR in a vrrp_instance block, since the latter only allows using linkbeat on the interface of the vrrp_instance itself, whereas \fRtrack_interface\fR and virtual_ipaddresses and virtual_iproutes may require monitoring other interfaces, which may need to use linkbeat polling. .PP The default polling type to use is MII, unless that isn't supported in which case ETHTOOL is used, and if that isn't supported then ioctl polling. The preferred type of polling to use can be specified with MII or ETHTOOL or IOCTL after the interface name, but if that type isn't supported, a supported type will be used. .PP The syntax for linkbeat_interfaces is: .nf \fBlinkbeat_interfaces\fR { eth2 enp2s0 ETHTOOL } .fi .SH Static track groups .PP Static track groups are used to allow vrrp instances to track static addresses, routes and rules. If a static address/route/rule specifies a track group, then if the address/route/rule is deleted and cannot be restored, the vrrp instance will transition to fault state. .PP The syntax for a track group is: .nf \fBtrack_group \fRGROUP1 { \fBgroup \fR{ VI_1 VI_2 } } .fi .SH Static routes/addresses/rules .PP Keepalived can configure static addresses, routes, and rules. These addresses, routes and rules are \fBNOT\fR moved by vrrpd, they stay on the machine. If you already have IPs and routes on your machines and your machines can ping each other, you don't need this section. The syntax for rules and routes is the same as for ip rule add/ip route add (except shortened option names are not supported due to ambiguities). The track_group specification refers to a named track_group which lists the vrrp instances which will track the address, i.e. if the address is deleted the vrrp instances will transition to backup. NOTE: since rules without preferences can be added in different orders due to vrrp instances transitioning from master to backup etc, rules need to have a preference. If a preference is not specified, keepalived will assign one, but it will probably not be what you want. .PP The syntax is the same for virtual addresses and virtual routes. If no dev element is specified, it defaults to default_interface (default eth0). Note: the broadcast address may be specified as '-' or '+' to clear or set the host bits of the address. .PP If a route or rule could apply to either IPv4 or IPv6 it will default to IPv4. To force a route/rule to be IPv6, add the keyword "inet6". .PP By default keepalived prepends routes (the kernel's default) which adds the route before any matching routes (this is the same behaviour as the (undocumented) 'ip route prepend' command). If 'add' is specified, the behaviour will be the same as the 'ip route add' command, which only adds the route if there is no matching route. If 'append' is specified, the behaviour is the same as the 'ip route append' command, i.e. the route is added after any matching route. Note: the rules for whether a route matches differ between IPv4 and IPv6; for example specifying a different proto means a matching route can be prepended/appended for IPv4 but not for IPv6. If in doubt, test it using the 'ip route add/prepend/append' commands. .PP .nf \fBstatic_ipaddress \fR{ [/] [brd ] [dev ] [scope ] [label