diff options
43 files changed, 4120 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..358c973 --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +*~ +.*.swp +*.[oa] +*.l[oa] +*.so +*.libs +*.deps +.dirstamp +libtool +/aclocal.m4 +/autom4te.cache +/build-stamp +/ChangeLog +/config +/config.log +/config.guess +/config.status +/config.sub +/configure +/debian/files +/debian/vyatta-cfg +/debian/vyatta-cfg.postinst +/debian/vyatta-cfg.postrm +/INSTALL +/Makefile.in +/Makefile +/src/my_commit +/src/my_set +/src/my_delete +/src/cli_def.c +/src/cli_parse.c +/src/cli_parse.h +/src/cli_val.c +/tools/rl_passwd + @@ -0,0 +1 @@ +eng@vyatta.com @@ -0,0 +1,27 @@ +/* + * Package: vyatta-wanloadbalance + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) "2007" Vyatta, Inc. + * All Rights Reserved. + * + * Author: eng@vyatta.com + * Date: 2007 + * Description: Vyatta wan load balancing project + * + * **** End License **** + * + */ diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..6f42120 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,22 @@ +initddir = /etc/init.d + +AM_CPPFLAGS = -I src -Wall -DDEBUG + +initd_SCRIPTS = etc/init.d/vyatta-ofr + +CLEANFILES = src/main.o src/lbdata.o src/lbdatafactory.o src/loadbalance.o src/rl_str_proc.o src/lbpathtest.o src/lboutput.o + + +sbin_PROGRAMS = src/wan_lb +src_wan_lb_SOURCES = src/main.cc +src_wan_lb_SOURCES += src/lbdata.cc +src_wan_lb_SOURCES += src/lbdatafactory.cc +src_wan_lb_SOURCES += src/loadbalance.cc +src_wan_lb_SOURCES += src/rl_str_proc.cc +src_wan_lb_SOURCES += src/lbpathtest.cc +src_wan_lb_SOURCES += src/lbdecision.cc +src_wan_lb_SOURCES += src/lboutput.cc + + +cpiop = find . ! -regex '\(.*~\|.*\.bak\|.*\.swp\|.*\#.*\#\)' -print0 | \ + cpio -0pd @@ -0,0 +1 @@ +see http://www.vyatta.com/news/ @@ -0,0 +1 @@ +This package has the Vyatta wan load balancing project. diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..38b00d0 --- /dev/null +++ b/configure.ac @@ -0,0 +1,36 @@ +# Process this file with autoconf to produce a configure script. +AC_PREREQ(2.59) + +m4_define([VERSION_ID], [m4_esyscmd([ + if test -f .version ; then + head -n 1 .version | tr -d \\n + else + echo -n 2.4 + fi])]) +AC_INIT([vyatta-wanloadbalance], VERSION_ID, [vyatta-support@vyatta.com]) + +test -n "$VYATTA_VERSION" || VYATTA_VERSION=$PACKAGE_VERSION + +AC_CONFIG_AUX_DIR([config]) +AM_INIT_AUTOMAKE([gnu no-dist-gzip dist-bzip2 subdir-objects]) +AC_PREFIX_DEFAULT([/opt/vyatta]) + +AC_PROG_CC +AM_PROG_AS +AM_PROG_CC_C_O +AC_PROG_LIBTOOL + +AC_ARG_ENABLE([nostrip], + AC_HELP_STRING([--enable-nostrip], + [include -nostrip option during packaging]), + [NOSTRIP=-nostrip], [NOSTRIP=]) + +AC_CONFIG_FILES( + [Makefile] + [debian/vyatta-wanloadbalance.postinst] + [debian/vyatta-wanloadbalance.postrm]) + +AC_SUBST(NOSTRIP) + +AC_OUTPUT + diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..9acb769 --- /dev/null +++ b/debian/README @@ -0,0 +1,6 @@ +The Debian Package vyatta-wanloadbalance +---------------------------- + +This package contains the wan load balance project developed by vyatta. This package includes the process, plus cli operational and commands. + + -- Michael Larson <mike@vyatta.com> 12/14/07 diff --git a/debian/autogen.sh b/debian/autogen.sh new file mode 100755 index 0000000..ff125d1 --- /dev/null +++ b/debian/autogen.sh @@ -0,0 +1,37 @@ +#!/bin/sh + + +if [ -d .git ] ; then +# generate GNU/Debian format ChangeLog from git log + + rm -f ChangeLog + + if which git2cl >/dev/null ; then + git-log --pretty --numstat --summary | git2cl >> ChangeLog + else + git-log --pretty=short >> ChangeLog + fi + +# append repository reference + + url=` git repo-config --get remote.origin.url` + test "x$url" = "x" && url=`pwd` + + branch=`git-branch --no-color | sed '/^\* /!d; s/^\* //'` + test "x$branch" = "x" && branch=master + + sha=`git log --pretty=oneline --no-color -n 1 | cut -c-8` + test "x$sha" = "x" && sha=00000000 + + echo "$url#$branch-$sha" >> ChangeLog + +fi + +rm -rf config +rm -f aclocal.m4 config.guess config.statusconfig.sub configure INSTALL + +autoreconf --force --install + +rm -f config.sub config.guess +ln -s /usr/share/misc/config.sub . +ln -s /usr/share/misc/config.guess . diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..0b71448 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +vyatta-wanloadbalance (0.1) unstable; urgency=low + + * Initial Release. + + -- Michael Larson <mike@vyatta.com> Fri, 14 Dec 2007 17:31:53 -0700 diff --git a/debian/changelog.bar b/debian/changelog.bar new file mode 100644 index 0000000..01b2125 --- /dev/null +++ b/debian/changelog.bar @@ -0,0 +1,5 @@ +vyatta-cfg (0.1) unstable; urgency=low + + * Initial Release. + + -- An-Cheng Huang <ancheng@vyatta.com> Mon, 24 Sep 2007 17:31:53 -0700 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..2835656 --- /dev/null +++ b/debian/control @@ -0,0 +1,24 @@ +Source: vyatta-wanloadbalance +Section: contrib/net +Priority: extra +Maintainer: Michael Larson <mike@vyatta.com> +Build-Depends: debhelper (>= 5), autotools-dev +Standards-Version: 3.7.2 + +Package: vyatta-wanloadbalance +Architecture: any +Depends: bash (>= 3.1), + sed (>= 4.1.5), + perl (>= 5.8.8), + procps (>= 1:3.2.7-3), + quagga, + coreutils (>= 5.97-5.3), + vyatta-config-migrate +Suggests: util-linux (>= 2.13-5), + net-tools, + ethtool, + ncurses-bin (>= 5.5-5), + ntpdate +Description: Vyatta configuration system + This package has the Vyatta wan load balance project. It includes support for + the vyatta cli for configuration. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..4de3df5 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,34 @@ +This package was debianized by Michael Larson <mike@vyatta.com> on +Mon, 24 Sep 2007 17:31:53 -0700. + +It's original content from the GIT repository <http://vyatta.com/git/vyatta-cfg> + +Upstream Author: + + <eng@vyatta.com> + +Copyright: + + Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + +License: + + The contents of this package are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. + +The Debian packaging is (C) 2007, Michael Larson <mike@vyatta.com> and +is licensed under the GPL, see above. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..50bd824 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +NEWS +README diff --git a/debian/linda b/debian/linda new file mode 100644 index 0000000..0381d9d --- /dev/null +++ b/debian/linda @@ -0,0 +1 @@ +Tag: file-in-opt diff --git a/debian/lintian b/debian/lintian new file mode 100644 index 0000000..feed37a --- /dev/null +++ b/debian/lintian @@ -0,0 +1,2 @@ +vyatta-wanloadbalance: file-in-unusual-dir +vyatta-wanloadbalance: dir-or-file-in-opt diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..244a67b --- /dev/null +++ b/debian/rules @@ -0,0 +1,101 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) +PACKAGE=vyatta-cfg +PKGDIR=$(CURDIR)/debian/$(PACKAGE) + +CFLAGS = -Wall -g + +configure = ./configure +configure += --host=$(DEB_HOST_GNU_TYPE) +configure += --build=$(DEB_BUILD_GNU_TYPE) +configure += --prefix=/opt/vyatta +configure += --mandir=\$${prefix}/share/man +configure += --infodir=\$${prefix}/share/info +configure += CFLAGS="$(CFLAGS)" +configure += LDFLAGS="-Wl,-z,defs" + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +configure: configure.ac Makefile.am + chmod +x debian/autogen.sh + debian/autogen.sh + +config.status: configure + dh_testdir + rm -f config.cache + $(configure) + +build: build-stamp + +build-stamp: config.status + dh_testdir + $(MAKE) + touch $@ + +clean: clean-patched + +# Clean everything up, including everything auto-generated +# at build time that needs not to be kept around in the Debian diff +clean-patched: + dh_testdir + dh_testroot + if test -f Makefile ; then $(MAKE) clean distclean ; fi + rm -f build-stamp + rm -f config.status config.sub config.guess config.log + rm -f aclocal.m4 configure Makefile.in Makefile INSTALL + rm -rf config + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + $(MAKE) DESTDIR=$(PKGDIR) install + + install -D --mode=0644 debian/lintian $(PKGDIR)/usr/share/lintian/overrides/$(PACKAGE) + install -D --mode=0644 debian/linda $(PKGDIR)/usr/share/linda/overrides/$(PACKAGE) + +# Build architecture-independent files here. +binary-indep: build install + dh_testdir + dh_testroot + dh_installchangelogs ChangeLog + dh_installdocs + dh_install + dh_installdebconf + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture-dependent files here. +binary-arch: build install +# This is an architecture independent package +# so; we have nothing to do by default. + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/debian/vyatta-wanloadbalance.postinst.in b/debian/vyatta-wanloadbalance.postinst.in new file mode 100644 index 0000000..21218c2 --- /dev/null +++ b/debian/vyatta-wanloadbalance.postinst.in @@ -0,0 +1,6 @@ +#!/bin/bash + +prefix=@prefix@ +sysconfdir=@sysconfdir@ + +#nothing diff --git a/debian/vyatta-wanloadbalance.postrm.in b/debian/vyatta-wanloadbalance.postrm.in new file mode 100644 index 0000000..262b9fe --- /dev/null +++ b/debian/vyatta-wanloadbalance.postrm.in @@ -0,0 +1,6 @@ +#!/bin/bash + +if [ "$1" = "purge" ]; then + update-rc.d vyatta-ofr remove >/dev/null || exit $? +fi + diff --git a/etc/bash_completion.d/20vyatta-cfg b/etc/bash_completion.d/20vyatta-cfg new file mode 100644 index 0000000..f116554 --- /dev/null +++ b/etc/bash_completion.d/20vyatta-cfg @@ -0,0 +1,892 @@ +# **** License **** +# Version: VPL 1.0 +# +# The contents of this file are subject to the Vyatta Public License +# Version 1.0 ("License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://www.vyatta.com/vpl +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2006, 2007 Vyatta, Inc. +# All Rights Reserved. +# +# Author: An-Cheng Huang +# Date: 2007 +# Description: bash completion for Vyatta configuration commands +# +# **** End License **** + +# only do this if we are going into configure mode +if [ "$_OFR_CONFIGURE" != "ok" ]; then + return 0 +fi + +umask 0002 + +if [ -r /etc/default/vyatta ]; then + source /etc/default/vyatta +fi + +declare is_set=0 +declare last_idx=0 +declare -a comp_words=() + +# commands to unalias +declare -a unalias_cmds=( clear configure date debug edit exit load \ + no run set show save terminal undebug ) +for cmd in "${unalias_cmds[@]}"; do + unalias $cmd >& /dev/null +done + +show () +{ + local show_all='' + local -a args=() + for arg in "$@"; do + if [ "$arg" == "-all" ]; then + show_all='-all' + else + args[${#args[@]}]="$arg" + fi + done + eval "${vyatta_sbindir}/vyatta-output-config.pl ${show_all}\ + \${VYATTA_EDIT_LEVEL//\// } ${args[@]}" +} + +save () +{ + eval "sudo sg quaggavty \ + \"umask 0002 ; ${vyatta_sbindir}/vyatta-save-config.pl $@\"" +} + +declare vyatta_cfg_prompt_level='' +set_config_ps1 () +{ + local level=$1 + if [ -z "$level" ]; then + export PS1="[edit]\n\u@\h# " + vyatta_cfg_prompt_level='' + else + export PS1="[edit $level]\n\u@\h# " + vyatta_cfg_prompt_level="$level" + fi +} + +load () +{ + # don't load if there are uncommitted changes. + if [ -f "$VYATTA_TEMP_CONFIG_DIR/$VYATTA_MOD_NAME" ]; then + echo "Cannot load: configuration modified." + echo "Commit or discard the changes before loading a config file." + return 1 + fi + # return to top level. + export VYATTA_EDIT_LEVEL="/" + export VYATTA_TEMPLATE_LEVEL="/" + set_config_ps1 '' + eval "${vyatta_sbindir}/vyatta-load-config.pl $@" +} + +edit () +{ + local num_comp=${#@} + local _mpath=${VYATTA_TEMP_CONFIG_DIR}/${VYATTA_EDIT_LEVEL} + local _tpath=${VYATTA_CONFIG_TEMPLATE}/${VYATTA_TEMPLATE_LEVEL} + local idx + for (( idx=1; idx <= num_comp; idx++ )); do + local comp + eval "comp=\$$idx" + vyatta_escape comp comp + push_path _mpath $comp + push_path _tpath $comp + if [ ! -d $_mpath ]; then + # "edit" only allows existing node + break + fi + + # check if it's not tag value + if [ -d $_tpath ]; then + continue + fi + + # check if it's tag value + pop_path _tpath + push_path _tpath $VYATTA_TAG_NAME + if [ -d $_tpath ]; then + continue + fi + pop_path _tpath + pop_path _mpath + break + done + # "edit" only valid for + # * "node.tag" level + # * "node.def" level without "type:" + if (( idx != ( num_comp + 1) )); then + echo "Invalid node \"$*\" for the 'edit' command" + return 1 + fi + if [ "${_tpath:((-9))}" != "/node.tag" ]; then + # we are not at "node.tag" level. look for "type:". + if [ ! -r "$_tpath/node.def" ]; then + vyatta_cfg_type="" + else + vyatta_parse_tmpl "$_tpath/node.def" + fi + if [ -n "$vyatta_cfg_type" ]; then + # "type:" present + echo "The 'edit' command cannot be issued at the \"$*\" level" + return 1 + fi + fi + export VYATTA_EDIT_LEVEL="${_mpath#$VYATTA_TEMP_CONFIG_DIR}/" + export VYATTA_TEMPLATE_LEVEL="${_tpath#$VYATTA_CONFIG_TEMPLATE}/" + + declare -a path_arr + path_str2arr VYATTA_EDIT_LEVEL path_arr + local path_str="${path_arr[*]}" + set_config_ps1 "$path_str" +} + +really_exit() +{ + sudo umount $VYATTA_TEMP_CONFIG_DIR + sudo rm -rf $VYATTA_TEMP_CONFIG_DIR $VYATTA_CHANGES_ONLY_DIR \ + $VYATTA_CONFIG_TMP + unset _OFR_CONFIGURE + builtin exit 0 +} + +exit () +{ + local discard + if [ $# == 0 ]; then + discard=0 + elif [ $# == 1 ] && [ "$1" == "discard" ]; then + discard=1 + else + echo "Invalid argument \"$*\" for 'exit'" + return 1 + fi + + if [ "$VYATTA_EDIT_LEVEL" == "/" ]; then + # we are at the root level. check if we can really exit. + if [ -f "$VYATTA_TEMP_CONFIG_DIR/$VYATTA_MOD_NAME" ]; then + if (( ! discard )); then + echo "Cannot exit: configuration modified." + echo "Use 'exit discard' to discard the changes and exit." + return 1 + fi + fi + really_exit + fi + + # "exit" to the root level. + export VYATTA_EDIT_LEVEL="/" + export VYATTA_TEMPLATE_LEVEL="/" + set_config_ps1 '' +} + +# run op mode commands +run () +{ + _vyatta_op_run $@ +} + +# do op mode completion +vyatta_run_complete () +{ + local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) + shopt -s extglob nullglob + + COMP_WORDS=( "${COMP_WORDS[@]:1}" ) + (( COMP_CWORD -= 1 )) + _vyatta_op_expand + + eval $restore_shopts +} + +declare v_cfg_completion_debug=0 +decho () +{ + if (( v_cfg_completion_debug )); then + echo -n "$*" + fi +} + +push_path_arr () +{ + # $1: \@path_arr + # $2: component + eval "$1=( \"\${$1[@]}\" '$2' )" +} + +pop_path_arr () +{ + # $1: \@path_arr + eval "$1=( \"\${$1[@]:0:((\${#$1[@]}-1))}\" )" +} + +path_arr2str () +{ + # $1: \@path_arr + # $2: \$path_str + eval "$2=\"\${$1[*]}\"" + eval "$2=/\${$2// //}" +} + +path_str2arr () +{ + # $1: \$path_str + # $2: \@path_arr + local tmp + eval "tmp=\${$1:1}" + eval "$2=( \${tmp//\// } )" +} + +push_path () +{ + # $1: \$path_str + # $2: component + declare -a path_arr + eval "path_str2arr $1 path_arr" + eval "push_path_arr path_arr '$2'" + eval "path_arr2str path_arr $1" +} + +pop_path () +{ + # $1: \$path_str + declare -a path_arr + eval "path_str2arr $1 path_arr" + pop_path_arr path_arr + eval "path_arr2str path_arr $1" +} + +get_filtered_dir_listing () +{ + # $1: path + # $2: \@listing + if [ ! -d $1 ]; then + eval "$2=()" + return + fi + local pattern='^node\.def$|^node\.tag$|^node\.val$|^\.modified$' + patterh=$pattern'|^\.commit\.lck$|^\.wh\.' + local cmd="ls $1 |egrep -v '$pattern'" + declare -a listing=( $(eval $cmd) ) + for enode in "${listing[@]}"; do + local unode + vyatta_unescape enode unode + eval "$2[\${#$2[@]}]=$unode" + done +} + +filter_existing_nodes () +{ + # $1: mpath + # $2: \@orig + # $3: \@filtered + declare -a orig + eval "orig=( \"\${$2[@]}\" )" + for node in "${orig[@]}"; do + if [ -d "$1/$node" ]; then + eval "$3[\${#$3[@]}]=$node" + fi + done +} + +get_prefix_filtered_list () +{ + # $1: prefix + # $2: \@list + # $3: \@filtered + declare -a olist + local pfx=$1 + pfx=${pfx#\"} + eval "olist=( \"\${$2[@]}\" )" + local idx=0 + for elem in "${olist[@]}"; do + local sub=${elem#$pfx} + if [ "$elem" == "$sub" ] && [ -n "$pfx" ]; then + continue + fi + eval "$3[$idx]=\"$elem\"" + (( idx++ )) + done +} + +vyatta_parse_tmpl_comp_fields () +{ + # $1: tmpl + # $2: field name + sed -n ' + /^#'"$2"':/,$ { + s/^#'"$2"':// + h + :b + $ { x; p; q } + n + /^#[-_a-z]\+:/ { x; p; q } + s/^#// + H + bb + } + ' $1 +} + +declare vyatta_cfg_help="" +declare vyatta_cfg_type="" +declare vyatta_cfg_tag=0 +declare vyatta_cfg_multi=0 +declare -a vyatta_cfg_allowed=() +declare vyatta_cfg_comp_help="" +vyatta_parse_tmpl () +{ + # $1: tmpl + vyatta_cfg_help="" + vyatta_cfg_type="" + vyatta_cfg_tag=0 + vyatta_cfg_multi=0 + vyatta_cfg_allowed=() + vyatta_cfg_comp_help='' + if [ ! -r $1 ]; then + return + fi + eval `sed -n ' + /^help:[ ]\+/,/^[a-z]\+:/ { + s/^help:[ ]\+/vyatta_cfg_help=/p + /^ /p + } + /^syntax:[ ]\+\$(@)[ ]\+in[ ]\+/ { + s/^syntax:[ ]\+\$(@)[ ]\+in[ ]\+/vyatta_cfg_allowed=( / + s/^\([^;]\+\);.*$/\1 )/ + s/[ ]*,[ ]*/ /gp + } + s/^tag:/vyatta_cfg_tag=1/p + s/^multi:/vyatta_cfg_multi=1/p + s/^type:[ ]\+\([^ ;]\+\)\(;.*\)\?/vyatta_cfg_type=\1/p + ' $1` + + local acmd=$(vyatta_parse_tmpl_comp_fields $1 "allowed") + vyatta_cfg_comp_help=$(vyatta_parse_tmpl_comp_fields $1 "comp_help") + + if (( ${#vyatta_cfg_allowed[@]} == 0 )); then + local ares=$(eval "$acmd") + eval "vyatta_cfg_allowed=( $ares )" + fi + if [ -z "$vyatta_cfg_help" ]; then + vyatta_cfg_help='<No help text available>' + fi +} + +# this fills in $vyatta_help_text +generate_help_text () +{ + # $1: \@items + # $2: \@help_strs + declare -a items + declare -a helps + eval "items=( \"\${$1[@]}\" )" + eval "helps=( \"\${$2[@]}\" )" + if [ -n "$vyatta_cfg_comp_help" ]; then + vyatta_help_text="\\n${vyatta_cfg_comp_help}" + return 0 + fi + vyatta_help_text="\\nPossible completions:" + for (( idx = 0; idx < ${#items[@]}; idx++ )); do + vyatta_help_text="${vyatta_help_text}\\n\\x20\\x20" + if [ ${#items[$idx]} -lt 6 ]; then + vyatta_help_text="${vyatta_help_text}${items[$idx]}\\t\\t" + elif [ ${#items[$idx]} -lt 14 ]; then + vyatta_help_text="${vyatta_help_text}${items[$idx]}\\t" + else + vyatta_help_text="${vyatta_help_text}${items[$idx]}\\n\\x20\\x20\\t\\t" + fi + vyatta_help_text="${vyatta_help_text}${helps[$idx]}" + done +} + +# this fills in $vyatta_help_text +get_tmpl_subdir_help () +{ + # $1: path + # $2: \@subdirs + declare -a subdirs + eval "subdirs=( \"\${$2[@]}\" )" + if [ ${#subdirs[@]} == 0 ]; then + vyatta_help_text="" + return + fi + declare -a hitems=() + declare -a hstrs=() + for subdir in "${subdirs[@]}"; do + if [ ! -r $1/$subdir/node.def ]; then + vyatta_cfg_help="<No help text available>" + else + vyatta_parse_tmpl "$1/$subdir/node.def" + # comp_help overrides the current help, so we reset it here since + # it is from the subdir. + vyatta_cfg_comp_help='' + fi + hitems[${#hitems[@]}]=$subdir + hstrs[${#hstrs[@]}]=$vyatta_cfg_help + done + generate_help_text hitems hstrs +} + +# return 0 if yes. 1 if no. +item_in_list () +{ + # $1: item + # $2: \@list + declare -a olist + local item + eval "olist=( \"\${$2[@]}\" )" + for item in "${olist[@]}"; do + if [ "$1" == "$item" ]; then + return 0 + fi + done + return 1 +} + +append_allowed_values () +{ + # $1: tmpl_path + # $2: \@values + if [ ! -r "$1/node.def" ]; then + return + fi + vyatta_parse_tmpl "$1/node.def" + local item + for item in "${vyatta_cfg_allowed[@]}"; do + if ! item_in_list "$item" $2; then + eval "$2[\${#$2[@]}]=\"$item\"" + fi + done +} + +# return 0 if yes. 1 if no. +is_setting_new_leaf () +{ + # $1: tmpl_path + if [ $is_set == 0 ]; then + return 1 + fi + vyatta_parse_tmpl "$1/node.def" + if [ -z "$vyatta_cfg_type" ]; then + return 1 + fi + return 0 +} + +# this fills in $vyatta_help_text +get_node_value_help () +{ + # $1: path + # $2: \@values + declare -a vals + eval "vals=( \"\${$2[@]}\" )" + if [ $is_set == 0 -a ${#vals[@]} == 0 ]; then + vyatta_help_text="" + return + fi + if [ ! -r "$1/node.def" ]; then + vyatta_cfg_help="<No help text available>" + vyatta_cfg_type="" + else + vyatta_parse_tmpl "$1/node.def" + fi + if [ $is_set == 1 -a ! -z "$vyatta_cfg_type" ]; then + # add a <type> value + local val="<$vyatta_cfg_type>" + vals=( $val "${vals[@]}" ) + fi + if [ ${#vals[@]} == 0 ]; then + vyatta_help_text="" + return + fi + declare -a hitems=() + declare -a hstrs=() + for val in "${vals[@]}"; do + hitems[${#hitems[@]}]=$val + hstrs[${#hstrs[@]}]=$vyatta_cfg_help + done + generate_help_text hitems hstrs +} + +get_value_list () +{ + # $1: path + # $2: \@listing + local vfile=$1/node.val + if [ ! -r $vfile ]; then + eval "$2=()" + return + fi + declare -a listing=() + local cmd=$(sed 's/^\(.*\)$/listing[\${#listing[@]}]='\''\1'\''/' $vfile) + eval "$cmd" + eval "$2=( \"\${listing[@]}\" )" +} + +vyatta_escape () +{ + # $1: \$original + # $2: \$escaped + eval "$2=\${$1//\%/%25}" + eval "$2=\${$2//\*/%2A}" + eval "$2=\${$2//\//%2F}" +} + +vyatta_unescape () +{ + # $1: \$escaped + # $2: \$original + eval "$2=\${$1//\%2F/\/}" + eval "$2=\${$2//\%2A/*}" + eval "$2=\${$2//\%25/%}" +} + +declare -a vyatta_completions +declare vyatta_help_text="\\nNo help text available" +declare vyatta_do_help=0 +vyatta_do_complete () +{ + # when this function is called, it is expected that: + # * "vyatta_help_text" is filled with the help text. + # * "vyatta_completions" is an array of "filtered" possible completions + # (i.e., only those starting with the current last component). + local do_help=$vyatta_do_help + + # we may not want to do the following +<<'ENDCOMMENT' + if [ ${#vyatta_completions[@]} == 1 ]; then + # no ambiguous completions. do completion instead of help. + do_help=0 + fi + + # now check if we can auto-complete at least 1 more character. + if (( do_help )); then + local schar="" + for comp in "${vyatta_completions[@]}"; do + local sub=$comp + if [ ! -z "${COMP_WORDS[COMP_CWORD]}" ]; then + sub=${comp#${comp_words[$last_idx]}} + if [ "$comp" == "$sub" ]; then + # should not happen since vyatta_completions should be filtered. + continue + fi + fi + if [ -z "$schar" ]; then + schar=${sub:0:1} + else + if [ "$schar" != "${sub:0:1}" ]; then + schar="" + break + fi + fi + done + if [ ! -z "$schar" ]; then + do_help=0 + fi + fi +ENDCOMMENT + + if (( do_help )); then + printf "$vyatta_help_text" + COMPREPLY=( "" " " ) + else + local -a f_comps=() + get_prefix_filtered_list "${COMP_WORDS[COMP_CWORD]}" \ + vyatta_completions f_comps + local estr="COMPREPLY=( " + for w in "${f_comps[@]}"; do + estr="$estr\"$w\" " + done + estr="${estr})" + eval "$estr" + fi + vyatta_help_text="\\nNo help text available" +} + +vyatta_config_complete () +{ + local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) + shopt -s extglob nullglob + + if [ "$COMP_LINE" == "$VYATTA_COMP_LINE" ]; then + VYATTA_COMP_LINE='' + vyatta_do_help=1 + else + VYATTA_COMP_LINE=$COMP_LINE + vyatta_do_help=0 + fi + + local command=${COMP_WORDS[0]} + # completion for "set" is different from other commands + is_set=0 + if [ "$command" == "set" ]; then + is_set=1 + fi + local end_space=0 + local num_comp=$COMP_CWORD + if [ -z "${COMP_WORDS[$COMP_CWORD]}" ]; then + end_space=1 + (( num_comp -= 1 )) + fi + (( last_idx = num_comp - 1 )) + comp_words=( ${COMP_WORDS[@]:1:$num_comp} ) + + # handle "exit" + if [ "$command" == "exit" ]; then + if (( num_comp > 1 || ( end_space && num_comp > 0 ) )); then + COMPREPLY=() + return + fi + declare -a hitems=( "discard" ) + declare -a hstrs=( "Discard any changes" ) + generate_help_text hitems hstrs + vyatta_completions=( "discard" ) + vyatta_do_complete + return + fi + + local _mpath=${VYATTA_TEMP_CONFIG_DIR}/${VYATTA_EDIT_LEVEL} + local _tpath=${VYATTA_CONFIG_TEMPLATE}/${VYATTA_TEMPLATE_LEVEL} + local last_tag=0 + local idx=0 + for (( idx=0; idx < num_comp; idx++ )); do + last_tag=0 + local comp=${comp_words[$idx]} + vyatta_escape comp comp + push_path _mpath $comp + push_path _tpath $comp + if [ -d $_tpath ]; then + if (( ! is_set )); then + # we are not in "set" => only allow existing node + if [ ! -d $_mpath ]; then + break + fi + fi + continue + fi + pop_path _tpath + push_path _tpath $VYATTA_TAG_NAME + if [ -d $_tpath ]; then + if (( ! is_set && end_space )); then + # we are not in "set" && last component is complete. + # => only allow existing tag value. + if [ ! -d $_mpath ]; then + break + fi + fi + if (( idx != last_idx )); then + # TODO validate value + # break if not valid + # XXX is this validation necessary? (set will validate anyway) + true + fi + last_tag=1 + continue + fi + pop_path _tpath + pop_path _mpath + break + done + # at the end of the loop, 3 possibilities: + # 1. (idx < last_idx): some component before the last is invalid + # => invalid command + # 2. (idx == last_idx): last component matches neither template nor node.tag + # => if end_space, then invalid command + # otherwise, may be an incomplete (non-tag) component, or incomplete + # "leaf value" + # => try matching dirs in _tpath or value(s) in _mpath/node.val + # 3. (idx == num_comp): the whole command matches templates/tags + if (( idx < last_idx || ( idx == last_idx && end_space ) )); then + # TODO error message? + COMPREPLY=() + return + fi + + declare -a matches + if (( idx == last_idx )); then + # generate possibile matches (dirs in _tpath, and "help" from + # node.def in each dir, or values in _mpath/node.val) + declare -a fmatches + if [ -f $_mpath/node.val ]; then + decho {1a} + get_value_list $_mpath matches + get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches + append_allowed_values $_tpath fmatches + get_node_value_help $_tpath fmatches + else + decho {1b} + # see if the last component is a new leaf node + fmatches=() + if is_setting_new_leaf $_tpath; then + append_allowed_values $_tpath fmatches + get_node_value_help $_tpath fmatches + else + # last component is a non-value node. look for child nodes. + if (( ! is_set )); then + # not "set". only complete existing nodes. + declare -a amatches=() + get_filtered_dir_listing $_tpath amatches + filter_existing_nodes $_mpath amatches matches + else + get_filtered_dir_listing $_tpath matches + fi + get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches + get_tmpl_subdir_help $_tpath fmatches + fi + fi + vyatta_completions=( "${fmatches[@]}" ) + vyatta_do_complete + return + fi + + if (( last_tag && end_space )); then + # if not "set", check _mpath (last component is the tag) is valid + # generate possible matches (dirs in _tpath, and "help" from node.def + # in each dir) + decho {2} + if [ $is_set == 1 -o -d $_mpath ]; then + if (( ! is_set )); then + # not "set". only complete existing nodes. + declare -a fmatches=() + get_filtered_dir_listing $_tpath fmatches + filter_existing_nodes $_mpath fmatches matches + else + get_filtered_dir_listing $_tpath matches + fi + get_tmpl_subdir_help $_tpath matches + vyatta_completions=( "${matches[@]}" ) + vyatta_do_complete + return + fi + return + fi + + if (( last_tag && !end_space )); then + # generate possible matches (dirs in _mpath, and "help" from node.def + # in dirs in _tpath) + decho {3} + pop_path _mpath + pop_path _tpath + get_filtered_dir_listing $_mpath matches + declare -a fmatches + get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches + append_allowed_values $_tpath fmatches + get_node_value_help $_tpath fmatches + vyatta_completions=( "${fmatches[@]}" ) + vyatta_do_complete + return + fi + + if (( !last_tag && end_space )); then + # generate possible matches + # 1. dirs in _tpath, and "help" from node.def in each dir + # 2. value(s) in _mpath/node.val (only if _tpath/node.def is "multi:") + # 3. dirs in _mpath (only if _tpath/node.def is "tag:") + if [ -d $_tpath/node.tag ]; then + # last component is a "tag name". look for tag values. + decho {4a} + get_filtered_dir_listing $_mpath matches + append_allowed_values $_tpath matches + get_node_value_help $_tpath matches + elif [ -f $_mpath/node.val ]; then + # last component is a leaf node. look for values. + decho {4b} + get_value_list $_mpath matches + append_allowed_values $_tpath matches + get_node_value_help $_tpath matches + else + decho {4c} + # see if the last component is a new leaf node + matches=() + if is_setting_new_leaf $_tpath; then + append_allowed_values $_tpath matches + get_node_value_help $_tpath matches + else + # last component is a non-value node. look for child nodes. + if (( ! is_set )); then + # not "set". only complete existing nodes. + declare -a fmatches=() + get_filtered_dir_listing $_tpath fmatches + filter_existing_nodes $_mpath fmatches matches + else + get_filtered_dir_listing $_tpath matches + fi + get_tmpl_subdir_help $_tpath matches + fi + fi + vyatta_completions=( "${matches[@]}" ) + vyatta_do_complete + return + fi + + if (( !last_tag && !end_space )); then + # generate possible matches (dirs in _tpath, and "help" from node.def + # in each dir) + decho {5} + pop_path _tpath + get_filtered_dir_listing $_tpath matches + declare -a fmatches + get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches + get_tmpl_subdir_help $_tpath fmatches + vyatta_completions=( "${fmatches[@]}" ) + vyatta_do_complete + return + fi + + eval $restore_shopts +} + +DEF_GROUP=quaggavty +make_vyatta_config_dir () +{ + sudo mkdir -m 0775 -p $1 + sudo chgrp ${DEF_GROUP} $1 +} + +make_vyatta_config_dir $VYATTA_ACTIVE_CONFIGURATION_DIR +make_vyatta_config_dir $VYATTA_CHANGES_ONLY_DIR +make_vyatta_config_dir $VYATTA_CONFIG_TMP +if [ ! -d $VYATTA_TEMP_CONFIG_DIR ]; then + make_vyatta_config_dir $VYATTA_TEMP_CONFIG_DIR + sudo mount -t unionfs -o dirs=${VYATTA_CHANGES_ONLY_DIR}=rw:/opt/vyatta/config/active=ro unionfs ${VYATTA_TEMP_CONFIG_DIR} +fi + +# disallow 'Ctrl-D' exit, since we need special actions on 'exit' +set -o ignoreeof 1 + +set_config_ps1 '' +alias commit=/opt/vyatta/sbin/my_commit +alias set=/opt/vyatta/sbin/my_set +alias delete=/opt/vyatta/sbin/my_delete + +export VYATTA_COMP_LINE="" + +# readline bindings +bind 'set show-all-if-ambiguous on' +if ! bind -p |grep -q '\\C-x\\C-t'; then + bind '"\C-x\C-t": kill-region' +fi +if ! bind -p |grep -q '\\C-x\\C-o'; then + bind '"\C-x\C-o": copy-region-as-kill' +fi + +complete -F vyatta_config_complete set +complete -F vyatta_config_complete delete +complete -F vyatta_config_complete show +complete -F vyatta_config_complete edit +complete -F vyatta_config_complete exit +complete -F vyatta_run_complete run + diff --git a/etc/init.d/vyatta-ofr b/etc/init.d/vyatta-ofr new file mode 100755 index 0000000..96770f6 --- /dev/null +++ b/etc/init.d/vyatta-ofr @@ -0,0 +1,214 @@ +#!/bin/bash +### BEGIN INIT INFO +# Provides: ofr +# Required-Start: $syslog $time $local_fs +# Required-Stop: $syslog $time $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: S 0 1 6 +# Short-Description: Vyatta Router +# Description: Debian init script for the Vyatta Router +### END INIT INFO +# **** License **** +# Version: VPL 1.0 +# +# The contents of this file are subject to the Vyatta Public License +# Version 1.0 ("License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://www.vyatta.com/vpl +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. +# All Rights Reserved. +# +# Author: Tom Grennan <tgrennan@vyatta.com> +# **** End License **** + +. /lib/lsb/init-functions + +: ${vyatta_env:=/etc/default/vyatta} +source $vyatta_env + +declare progname=${0##*/} +declare action=$1; shift + +declare -x BOOTFILE=$vyatta_sysconfdir/config/config.boot + +declare -a subinit +declare -a all_subinits=( + rl-system + firewall + dhcpd + dhcrelay + lighttpd ) + +if [ $# -gt 0 ] ; then + for s in $@ ; do + [ -x ${vyatta_sbindir}/${s}.init ] && subinit[${#subinit}]=$s + done +else + for s in ${all_subinits[@]} ; do + [ -x ${vyatta_sbindir}/${s}.init ] && subinit[${#subinit}]=$s + done +# NOTE: rtrmgr (i.e. xorp) is mutually exclusive of quagga + if [ -x /etc/init.d/quagga ] ; then + # quagga should already be started. + GROUP=quaggavty + elif [ -x ${vyatta_sbindir}/rtrmgr.init ] ; then + subinit[${#subinit}]=rtrmgr + GROUP=xorp + fi +fi + + +have_rl_system () { + test -x $vyatta_sbindir/rl-system.init +} + +use_floppy () { + _fd_=/media/floppy + + if have_rl_system ; then +## only do this if we have the rl-system package +## if we donot discover an fd device, try loading the floppy module + grep -q fd /proc/devices || modprobe -q floppy 2>/dev/null + if grep -q fd /proc/devices ; then + if awk -- '$1 ~ /\/dev\/fd/ { exit 1 }' /proc/mounts + then + mkdir -p $_fd_ + mount -t ext2 /dev/fd0 $_fd_ -o sync 2>/dev/null + test $? -eq 32 && mount -t vfat /dev/fd0 $_fd_ 2>/dev/null + fi + fi + fi + test -d ${_fd_}/config +} + +setup_configdir() { + [ -f $BOOTFILE ] && return 0 + mkdir -m 0775 -p ${vyatta_sysconfdir}/config + chgrp ${GROUP} ${vyatta_sysconfdir}/config + if [ -d ~root/vyatta/config ] ; then + mount -o bind ~root/vyatta/config $vyatta_sysconfdir/config + elif use_floppy ; then + mount -o bind /media/floppy/config $vyatta_sysconfdir/config + else + mkdir -m 0775 -p ~root/vyatta/config + chgrp ${GROUP} ~root/vyatta ~root/vyatta/config + mount -o bind ~root/vyatta/config $vyatta_sysconfdir/config + fi +} + +setup_rclocal () { + [ -f /etc/rc.local ] || touch /etc/rc.local + if [ -f ~root/vyatta/rc.local ]; then + mount -o bind ~root/vyatta/rc.local /etc/rc.local + elif [ -f /media/floppy/rc.local ]; then + mount -o bind /media/floppy/rc.local /etc/rc.local + fi +} + +restore_rclocal () { + umount /etc/rc.local >& /dev/null +} + +# if necessary, provide initial config +init_bootfile () { + if [ ! -f $BOOTFILE ] ; then + if [ -f $vyatta_sysconfdir/config.boot.default ]; then + cp $vyatta_sysconfdir/config.boot.default $BOOTFILE + else + $vyatta_sbindir/vyatta_current_conf_ver.pl > $BOOTFILE + fi + fi + chgrp ${GROUP} $BOOTFILE + chmod 660 $BOOTFILE + ## remove the unnecessary and potentially conflicting + ## config-directory statement (i.e. /mnt/floppy vs. /media/floppy + sed -i '/^rtrmgr {$/,/^}$/d' $BOOTFILE +} + +# if necessary, migrate initial config +migrate_bootfile () +{ + if [ -x $vyatta_sbindir/vyatta_config_migrate.pl ]; then + log_progress_msg migrate + $vyatta_sbindir/vyatta_config_migrate.pl $BOOTFILE + fi +} + +# load the initial config +load_bootfile () +{ + if [ -x $vyatta_sbindir/vyatta-config-loader.pl ]; then + log_progress_msg configure + sg ${GROUP} -c "$vyatta_sbindir/vyatta-config-loader.pl $BOOTFILE" + fi +} + +# this handles the "config dir" (/opt/vyatta/config), which is different +# from the directory for config files (/opt/vyatta/etc/config). +mount_cfg_dir () +{ + if [ ! -d ${vyatta_configdir} ] ; then + mkdir -m 0775 -p ${vyatta_configdir} + chgrp ${GROUP} ${vyatta_configdir} + fi + mount -o nosuid,nodev -t tmpfs none ${vyatta_configdir} +} + +unmount_cfg_dir () +{ + umount ${vyatta_configdir} +} + +start () +{ + log_daemon_msg "Starting Vyatta Router" + setup_configdir + setup_rclocal + mount_cfg_dir + init_bootfile + migrate_bootfile + for s in ${subinit[@]} ; do + log_progress_msg $s + ${vyatta_sbindir}/${s}.init start || (log_end_msg $? && return) + done + load_bootfile + echo -n $BOOTFILE > ${vyatta_sysconfdir}/bootfile_path + log_end_msg $? +} + +stop() +{ + local -i status=0 + log_daemon_msg "Stopping Vyatta Router" + for ((i=${#sub_inits[@]} - 1; i >= 0; i--)) ; do + s=${subinit[$i]} + log_progress_msg $s + ${vyatta_sbindir}/${s}.init stop + let status\|=$? + done + unmount_cfg_dir + restore_rclocal + log_end_msg $status +} + +case "$action" in + start) start ;; + stop) stop ;; + restart|force-reload) stop && start ;; + *) log_failure_msg "usage: $progname [ start|stop|restart ] [ subinit ... ]" ; + false ;; +esac + +exit $? + +# Local Variables: +# mode: shell-script +# sh-indentation: 4 +# End: diff --git a/src/.#hosttool.cpp b/src/.#hosttool.cpp new file mode 120000 index 0000000..ea4c1f7 --- /dev/null +++ b/src/.#hosttool.cpp @@ -0,0 +1 @@ +slioch@eng-140.vyatta.com.8654:1196098102
\ No newline at end of file diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..a2b4c94 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,9 @@ +cli_parse.tab.c +cli_parse.tab.h +cli_def.lex.c +cli_def.tab.c +cli_def.tab.h +cli_val.lex.c +delete +my_* +show diff --git a/src/hosttool.cpp b/src/hosttool.cpp new file mode 100644 index 0000000..4849d03 --- /dev/null +++ b/src/hosttool.cpp @@ -0,0 +1,395 @@ +/** + + * Module: Hosts + + * Description: Host IP and last response time for all responding hosts + + * on this LAN segment. This tool uses a broadcast icmp test packet. + + * + + * Note that hosts can also be identified via ARP progressing through + + * all possible addresses on lan segment. + + * + + * Author: Michael Larson + + * email: mike(at)lrlart.com + + * Date: August 2004 + + **/ + + + +#include <ostream.h> + +#include <sys/time.h> + +#include <sys/types.h> + +#include <unistd.h> + +#include <sys/socket.h> + +#include <netinet/udp.h> + +#include <netinet/in.h> + +#include <netinet/ip.h> + +#include <netinet/ip_icmp.h> + +#include <errno.h> + +#include <memory> + +#include <time.h> + +#include <sys/timeb.h> + +#include <pthread.h> + +#include <stdio.h> + +#include <stdlib.h> + +#include <iostream.h> + +#include <string> + +#include <algorithm> + + + +#include "Hosts.hpp" + +#include "HostsResult.hpp" + +#include "HostsTool.hpp" + + + +//constant initialization + +const int HostsToolKonstants::packet_data_len_ = 40; + +const int HostsToolKonstants::recv_timeout_ = 5; + +const int HostsToolKonstants::ip_offset_ = 12; + + + +/** + + * HostsTool::HostsTool() + + * Constructor. Builds socket for use in tests. + + * + + **/ + +HostsTool::HostsTool(Task<Test> *complete_task, unsigned long local_ip, unsigned long bc_addr) : + + ToolBase(complete_task), + + local_ip_(local_ip), + + send_sock_(0), + + recv_sock_(0), + + bc_addr_(bc_addr), + + packet_id_(0), + + test_in_progress_(false) + +{ + + sockaddr_in addr; + + + + struct protoent *ppe = getprotobyname("icmp"); + + send_sock_ = socket(PF_INET, SOCK_RAW, ppe->p_proto); + + if (send_sock_ < 0) + + { + + cerr << "HostsTool::HostsTool(): no send sock: " << send_sock_ << endl; + + send_sock_ = 0; + + return; + + } + + + + //set options for broadcasting. + + int val = 1; + + setsockopt(send_sock_, SOL_SOCKET, SO_BROADCAST, &val, 4); + + setsockopt(send_sock_, SOL_SOCKET, SO_REUSEADDR, &val, 4); + + + + memset( &addr, 0, sizeof( struct sockaddr_in )); + + addr.sin_family = AF_INET; + + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + addr.sin_port = 0; + + + + recv_sock_ = socket(PF_INET, SOCK_RAW, ppe->p_proto); + + if (recv_sock_ < 0) + + { + + cerr << "HostsTool::HostsTool(): no recv sock: " << recv_sock_ << endl; + + recv_sock_ = 0; + + return; + + } + +} + + + +/** + + * HostsTool::~HostsTools() + + * Destructor, cleans up sockets + + * + + **/ + +HostsTool::~HostsTool() + +{ + + if (send_sock_ != 0) + + close(send_sock_); + + + + if (recv_sock_ != 0) + + close(recv_sock_); + +} + + + + + +/** + + * HostsTool::compute() + + * initiates tests received in its message queue. + + * + + **/ + +void + +HostsTool::compute() + +{ + + while (true) + + { + + auto_ptr<Test> test(get()); //don't bother keeping this Test object.. + + cout << "HostsTool::compute(): received test event..." << endl; + + if (test.get() != NULL) + + { + + GUARD(&mutex_); //protect against concurrent access to test_in_progress_ flag + + /* + + Note that on heavily cycled tests some tests may be dropped. This can be fixed + + by monitoring input queue on completion of sending and processing all incoming + + messages into a set. This isn't expected to be a problem with the current impl. + + */ + + if (test_in_progress_ == true) + + continue; + + if (test->get_target() == 0) + + { + + send(bc_addr_, packet_id_++); + + } + + else + + { + + send(test->get_target(), packet_id_++); + + } + + } + + } + +} + + + +/** + + * HostsTool::finish() + + * processes completed tests and pushes results to manager + + * + + **/ + +void + +HostsTool::finish() + +{ + + while (true) + + { + + HostsResult *host_result = receive(); + + // cout << "HostsTool::finish(): received result: " << host_result << endl; + + if (host_result != NULL) + + { + + GUARD(&mutex_); + + test_in_progress_ = false; + + if (host_result->empty() == false) + + { + + Test *test = new Test(kHosts); + + test->set_result(host_result); + + // cout << "HostsTool::finish(), dispatching result" << endl; + + results(test); + + } + + } + + } + +} + + + +/** + + * HostsTool::send() + + * pushes the icmp packet out on the wire. + + * + + **/ + +void + +HostsTool::send(unsigned long target_addr, unsigned short packet_id) + +{ + + int err; + + ->type) << endl; + +} + +} + + else + + { + + cerr << "HostsTool::receive(): error from recvfrom" << endl; + + } + +} + +//then push result upwards + +return host_result; + +} + + + +/** + + * HostsTool::in_checksum() + + * computes checksum that accompanies sending icmp packet + + * + + **/ + +unsigned short + +HostsTool::in_checksum(const unsigned short *buffer, int length) const + +{ + + unsigned long sum; + + for (sum=0; length>0; length--) + + sum += *buffer++; + + sum = (sum >> 16) + (sum & 0xffff); + + sum += (sum >> 16); + + return ~sum; + +} diff --git a/src/hosttool.h b/src/hosttool.h new file mode 100644 index 0000000..06d03a0 --- /dev/null +++ b/src/hosttool.h @@ -0,0 +1,86 @@ +/** + * Module: HostsTool + * Description: Collects lists of hosts on the network responding to broadcast icmp. + * This list of hosts are then dispatched for processing of results + * + * Author: Michael Larson + * email: mike(at)lrlart.com + * Date: August 2004 + **/ +#ifndef HOSTSTOOL_HPP_ +#define HOSTSTOOL_HPP_ + +//forward decls +class HostsResult; + +//header includes +#include <vector> +#include "Task.hpp" +#include "Test.hpp" +#include "ToolBase.hpp" + +/********************************************* + ** HostsToolKonstants ** + *********************************************/ +class HostsToolKonstants +{ + public: + static const int packet_data_len_; + static const int recv_timeout_; + static const int ip_offset_; +}; + +/********************************************* + ** HostsTool ** + *********************************************/ +class HostsTool : public ToolBase +{ + public: + /* + * Constructor and Destructor + */ + HostsTool(Task<Test> *pCompleteTask, unsigned long local_ip, unsigned long bc_addr_); + ~HostsTool(); + + /* + * compute test result + */ + void + compute(); + + /* + * finished tests + */ + void + finish(); + + /* + * send a new test + */ + void + send(unsigned long target_addr, unsigned short packet_id); + + /* + * receive results + */ + HostsResult* + receive(); + + private: + /* + * checksum for icmp packets + */ + unsigned short + in_checksum(const unsigned short *addr, int len) const; + + private: + unsigned long local_ip_; + int send_sock_; + int recv_sock_; + unsigned long bc_addr_; + int packet_id_; + bool test_in_progress_; + PERF_MUTEX mutex_; +}; + +#endif //HOSTSTOOL_HPP_ diff --git a/src/lbdata.cc b/src/lbdata.cc new file mode 100644 index 0000000..14a04c0 --- /dev/null +++ b/src/lbdata.cc @@ -0,0 +1,212 @@ +/* + * Module: lbdata.cc + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#include <sys/time.h> +#include <time.h> +#include <iostream> + +#include "lbdata.hh" + +int LBHealthHistory::_buffer_size = 10; + +/** + * + * + **/ +void +LBHealth::put(int rtt) +{ + int activity_ct = _hresults.push(rtt); + + if (rtt == -1) { + if (activity_ct >= _failure_ct) { + if (_is_active == true) { + _state_changed = true; + } + _is_active = false; + } + } + else { + if (activity_ct >= _success_ct) { + if (_is_active == false) { + _state_changed = true; + } + _is_active = true; + } + } +} + +/** + * + * + **/ +LBHealthHistory::LBHealthHistory(int buffer_size) : + _last_success(0), + _last_failure(0), + _index(0) +{ + _resp_data.resize(10); + + for (int i = 0; i < _buffer_size; ++i) { + _resp_data[i] = 0; + } +} + + + +/** + * + * + **/ +int +LBHealthHistory::push(int rtt) +{ + struct timeval tv; + gettimeofday(&tv,NULL); + + if (rtt == -1) { + _last_failure = tv.tv_sec; + } + else { + _last_success = tv.tv_sec; + } + + _resp_data[_index % _buffer_size] = rtt; + ++_index; + + //compute count of sequence of same responses + int ct = 0; + int start_index = (_index - 1) % _buffer_size; + for (int i = 0; i < _buffer_size; ++i) { + int index = start_index - i; + if (index < 0) { + //handle wrap around of index here + index = _buffer_size - index; + } + + if (_resp_data[index] == -1 && rtt == -1) { + ++ct; + } + else if (_resp_data[index] != -1 && rtt != -1) { + ++ct; + } + else { + return ct; + } + } + return ct; +} + +/** + * + * + **/ +bool +LBData::is_active(const string &iface) +{ + InterfaceHealthIter iter = _iface_health_coll.find(iface); + if (iter != _iface_health_coll.end()) { + return iter->second._is_active; + } + return false; +} + +/** + * + * + **/ +void +LBData::dump() +{ + + cout << "health" << endl; + LBData::InterfaceHealthIter h_iter = _iface_health_coll.begin(); + while (h_iter != _iface_health_coll.end()) { + cout << " " << h_iter->first << endl; + cout << " " << h_iter->second._success_ct << endl; + cout << " " << h_iter->second._failure_ct << endl; + cout << " " << h_iter->second._ping_target << endl; + cout << " " << h_iter->second._ping_resp_time << endl; + ++h_iter; + } + + cout << endl << "wan" << endl; + LBData::LBRuleIter r_iter = _lb_rule_coll.begin(); + while (r_iter != _lb_rule_coll.end()) { + cout << " rule: " << r_iter->first << endl; + cout << " " << r_iter->second._proto << endl; + cout << " " << r_iter->second._s_addr << endl; + cout << " " << r_iter->second._s_net << endl; + cout << " " << r_iter->second._s_port_num << endl; + cout << " " << r_iter->second._s_port_name << endl; + + cout << " " << r_iter->second._d_addr << endl; + cout << " " << r_iter->second._d_net << endl; + cout << " " << r_iter->second._d_port_num << endl; + cout << " " << r_iter->second._d_port_name << endl; + + LBRule::InterfaceDistIter ri_iter = r_iter->second._iface_dist_coll.begin(); + while (ri_iter != r_iter->second._iface_dist_coll.end()) { + cout << " interface: " << ri_iter->first << endl; + cout << " weight: " << ri_iter->second << endl; + ++ri_iter; + } + ++r_iter; + } + cout << "end dump" << endl; +} + +/** + * + * + **/ +bool +LBData::state_changed() +{ + LBData::InterfaceHealthIter h_iter = _iface_health_coll.begin(); + while (h_iter != _iface_health_coll.end()) { + if (h_iter->second.state_changed()) { + return true; + } + ++h_iter; + } + return false; +} + +/** + * + * + **/ +void +LBData::reset_state_changed() +{ + LBData::InterfaceHealthIter h_iter = _iface_health_coll.begin(); + while (h_iter != _iface_health_coll.end()) { + h_iter->second._state_changed = false; + ++h_iter; + } +} diff --git a/src/lbdata.hh b/src/lbdata.hh new file mode 100644 index 0000000..6d5c5af --- /dev/null +++ b/src/lbdata.hh @@ -0,0 +1,146 @@ +/* + * Module: lbdata.hh + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#ifndef __LBDATA_HH__ +#define __LBDATA_HH__ + +#include <map> +#include <set> +#include <vector> +#include <string> + +using namespace std; + +class LBRule { + public: + typedef map<string, int> InterfaceDistColl; + typedef map<string, int>::iterator InterfaceDistIter; + + typedef enum {ALL,ICMP,UDP,TCP} Protocol; + + LBRule() : + _proto("all") + {} + + public: + string _proto; + string _s_addr; + string _s_net; + string _s_port_num; + string _s_port_name; + + string _d_addr; + string _d_net; + string _d_port_num; + string _d_port_name; + + InterfaceDistColl _iface_dist_coll; +}; + + +class LBHealthHistory { +public: + LBHealthHistory(int buffer_size); + + //push in the ping response for this... + int push(int rtt); + + +public: + //results of health testing + unsigned long _last_success; + unsigned long _last_failure; + + static int _buffer_size; + vector<int> _resp_data; + int _index; +}; + +class LBHealth { + public: + LBHealth() : + _success_ct(0), + _failure_ct(0), + _ping_resp_time(0), + _hresults(10), + _is_active(true), + _state_changed(true), + _last_success(0), + _last_failure(0) + {} + + void put(int rtt); + + bool + state_changed() {return _state_changed;} + + int _success_ct; + int _failure_ct; + string _ping_target; + int _ping_resp_time; + LBHealthHistory _hresults; + bool _is_active; + bool _state_changed; + unsigned long _last_success; + unsigned long _last_failure; +}; + + +class LBData { + public: + typedef map<int,LBRule> LBRuleColl; + typedef map<int,LBRule>::iterator LBRuleIter; + typedef map<int,LBRule>::const_iterator LBRuleConstIter; + typedef map<string,LBHealth> InterfaceHealthColl; + typedef map<string,LBHealth>::iterator InterfaceHealthIter; + typedef map<string,LBHealth>::const_iterator InterfaceHealthConstIter; + + LBData() {} + + bool + error() {return false;} + + bool + is_active(const string &iface); + + bool + state_changed(); + + void + reset_state_changed(); + + void + dump(); + + public: + string _filename; + + LBRuleColl _lb_rule_coll; + InterfaceHealthColl _iface_health_coll; +}; + +#endif //__LBDATA_HH__ diff --git a/src/lbdatafactory.cc b/src/lbdatafactory.cc new file mode 100644 index 0000000..0220416 --- /dev/null +++ b/src/lbdatafactory.cc @@ -0,0 +1,334 @@ +/* + * Module: lbdatafactory.cc + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#include <syslog.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string> +#include <algorithm> +#include <vector> +#include <iostream> +#include "rl_str_proc.hh" +#include "lbdata.hh" +#include "lbdatafactory.hh" + +using namespace std; + + +LBDataFactory::LBDataFactory() +{ +} + +LBDataFactory::~LBDataFactory() +{ + +} + +bool +LBDataFactory::load(const string &conf_file) +{ + //open file + FILE *fp = fopen(conf_file.c_str(), "r"); + if (fp == NULL) { + cerr << "Error opening configuration file: " << conf_file << endl; + syslog(LOG_ERR, "wan_lb: configuration file not found: %s", conf_file.c_str()); + return false; + } + + //read line by line and populate vect + char str[1025]; + int depth(0); + vector<string> path(10); + while (fgets(str, 1024, fp) != 0) { + string line(str); + + int pos = line.find("#"); + line = line.substr(0,pos); + + string key,value; + + StrProc tokens(line, " "); + for (int i = 0; i < tokens.size(); ++i) { + string symbol = tokens.get(i); + + if (symbol != "{" && symbol != "}") { + if (key.empty()) { + key = symbol; + } + else if (value.empty()) { + value = symbol; + } + path[depth] = key; + } + else if (symbol == "{") { + ++depth; + } + else if (symbol == "}") { + --depth; + } + } + if (tokens.size() != 0) { + process(path,depth,key,value); + } + if (depth > 9 || depth < 0) { + cerr << "configuration error: malformed configuration file: brackets" << endl; + syslog(LOG_ERR, "wan_lb: malformed configuration file: brackets"); + return false; + } + } + + fclose(fp); + if (depth != 0) { + cerr << "configuration error: mismatched brackets in configuration file" << endl; + syslog(LOG_ERR, "wan_lb: configuration error due to mismatched brackets"); + return false; + } + +#ifdef DEBUG + _lb_data.dump(); +#endif + return true; +} + + +void +LBDataFactory::process(const vector<string> &path, int depth, const string &key, const string &value) +{ + string l_key, l_value; + std::transform(key.begin(), key.end(), std::back_inserter(l_key), + static_cast < int(*)(int) > (std::tolower)); + std::transform(value.begin(), value.end(), std::back_inserter(l_value), + static_cast < int(*)(int) > (std::tolower)); + if (path[0] == "health") { + if (l_key == "interface") { + process_health(l_key,l_value); + } + else { + process_health_interface(l_key,l_value); + } + } + else if (path[0] == "rule") { + if (depth > 0 && path[1] == "source") { + process_rule_source(l_key,l_value); + } + else if (depth > 0 && path[1] == "destination") { + process_rule_destination(l_key,l_value); + } + else if (depth > 1 && path[1] == "interface") { + process_rule_interface(l_key,l_value); + } + else if (depth > 0 && path[1] == "protocol") { + process_rule_protocol(l_key,l_value); + } + else { + process_rule(l_key,l_value); + } + } +} + + +void +LBDataFactory::process_health(const string &key, const string &value) +{ + if (value.empty() == false) { + LBData::InterfaceHealthIter iter = _lb_data._iface_health_coll.find(key); + if (iter == _lb_data._iface_health_coll.end()) { + _lb_data._iface_health_coll.insert(pair<string,LBHealth>(value,LBHealth())); + } + _health_iter = _lb_data._iface_health_coll.find(value); + } +} + + +void +LBDataFactory::process_health_interface(const string &key, const string &value) +{ + if (key == "target") { + _health_iter->second._ping_target = value; + } + else if (key == "success-ct") { + int num = strtoul(value.c_str(), NULL, 10); + if (num > 0) { + _health_iter->second._success_ct = num; + } + else { + cerr << "illegal success-ct specified: " << value << endl; + syslog(LOG_ERR, "wan_lb: illegal success-ct specified in configuration file: %s", value.c_str()); + } + } + else if (key == "failure-ct") { + int num = strtoul(value.c_str(), NULL, 10); + if (num > 0) { + _health_iter->second._failure_ct = num; + } + else { + cerr << "illegal failure-ct specified: " << value << endl; + syslog(LOG_ERR, "wan_lb: illegal failure-ct specified in configuration file: %s", value.c_str()); + } + } + else if (key == "ping-resp") { + int num = strtoul(value.c_str(), NULL, 10); + if (num > 0) { + _health_iter->second._ping_resp_time = num; + } + else { + cerr << "illegal ping-resp specified: " << value << endl; + syslog(LOG_ERR, "wan_lb: illegal ping-resp specified in configuration file: %s", value.c_str()); + } + } + else if (key == "health") { + //nothing + } + else { + cout << "LBDataFactory::process_health(): " << "don't understand this symbol: " << key << endl; + //nothing + } + +} + +void +LBDataFactory::process_rule(const string &key, const string &value) +{ + if (key.empty()) { + return; + } +#ifdef DEBUG + cout << "LBDataFactor::process_rule(): " << key << ", " << value << endl; +#endif + int num = strtoul(value.c_str(), NULL, 10); + if (num > 0) { + _lb_data._lb_rule_coll.insert(pair<int,LBRule>(num,LBRule())); + } + else { + cerr << "Rule number: illegal value" << endl; + syslog(LOG_ERR, "wan_lb: illegal rule number: %s", value.c_str()); + return; + } + _rule_iter = _lb_data._lb_rule_coll.find(num); +} + +void +LBDataFactory::process_rule_protocol(const string &key, const string &value) +{ + if (key == "protocol") { + if (strcasecmp(value.c_str(),"ALL") == 0) { + _rule_iter->second._proto = "all"; + } + else if (strcasecmp(value.c_str(),"ICMP") == 0) { + _rule_iter->second._proto = "icmp"; + } + else if (strcasecmp(value.c_str(), "UDP") == 0) { + _rule_iter->second._proto = "udp"; + } + else if (strcasecmp(value.c_str(),"TCP") == 0) { + _rule_iter->second._proto = "tcp"; + } + else { + cerr << "protocol not recognized: " << key << ", " << value << endl; + syslog(LOG_ERR, "wan_lb: illegal protocol specified: %s", value.c_str()); + } + } +} + +void +LBDataFactory::process_rule_source(const string &key, const string &value) +{ + if (key == "address") { + if (inet_addr(value.c_str()) == (unsigned)-1) { + cerr << "malformed ip address: " << key << ", " << value << endl; + syslog(LOG_ERR, "wan_lb, malformed ip address in configuration: %s,%s", key.c_str(),value.c_str()); + return; + } + _rule_iter->second._s_addr = value; + } + else if (key == "network") { + _rule_iter->second._s_net = value; + } + else if (key == "port-name") { + _rule_iter->second._s_port_num = value; + } + else if (key == "port-number") { + _rule_iter->second._s_port_name = value; + } +} + +void +LBDataFactory::process_rule_destination(const string &key, const string &value) +{ + if (key == "address") { + if (inet_addr(value.c_str()) == (unsigned)-1) { + cerr << "malformed ip address: " << key << ", " << value << endl; + syslog(LOG_ERR, "wan_lb, malformed ip address in configuration: %s,%s", key.c_str(),value.c_str()); + return; + } + _rule_iter->second._d_addr = value; + } + else if (key == "network") { + _rule_iter->second._d_net = value; + } + else if (key == "port-name") { + _rule_iter->second._d_port_num = value; + } + else if (key == "port-number") { + _rule_iter->second._d_port_name = value; + } +} + +void +LBDataFactory::process_rule_interface(const string &key, const string &value) +{ +#ifdef DEBUG + cout << "LBDataFactory::process_rule_interface(): " << key << ", " << value << endl; +#endif + if (key == "interface") { + _rule_iter->second._iface_dist_coll.insert(pair<string,int>(value,0)); + _rule_iface_iter = _rule_iter->second._iface_dist_coll.find(value); + } + else if (key == "weight") { + int num = strtoul(value.c_str(), NULL, 10); + if (num > 0) { + _rule_iface_iter->second = num; + } + else { + cerr << "illegal interface weight specified: " << value << endl; + syslog(LOG_ERR, "wan_lb: illegal interface weight specified in configuration file: %s", value.c_str()); + } + } + else { + cerr << "LBDataFactory::process_rule(): " << "don't understand this symbol: " << key << endl; + } +} + + + +LBData +LBDataFactory::get() +{ + return _lb_data; +} + diff --git a/src/lbdatafactory.hh b/src/lbdatafactory.hh new file mode 100644 index 0000000..29fe609 --- /dev/null +++ b/src/lbdatafactory.hh @@ -0,0 +1,94 @@ +/* + * Module: lbdatafactory.hh + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#ifndef __LBCONFLOADER_HH__ +#define __LBCONFLOADER_HH__ + +#include <string> +#include <vector> + +#include "lbdata.hh" + +using namespace std; + +class LBDataFactory { +public: + typedef vector<string> ConfColl; + typedef vector<string>::iterator ConfIter; + +public: + LBDataFactory(); + ~LBDataFactory(); + + bool + load(const string &conf_file); + + LBData + get(); + +private: + //parsing goes on here + void + tokenize(const string& str, + vector<string>& tokens, + const string& delimiters = " "); + + void + process(const vector<string> &path, int depth, const string &key, const string &value); + + void + process_health(const string &key, const string &value); + + void + process_health_interface(const string &key, const string &value); + + void + process_rule(const string &key, const string &value); + + void + process_rule_protocol(const string &key, const string &value); + + void + process_rule_source(const string &key, const string &value); + + void + process_rule_destination(const string &key, const string &value); + + void + process_rule_interface(const string &key, const string &value); + +private: + LBHealth _lb_health; + LBRule _lb_rule; + LBData _lb_data; + + LBData::LBRuleIter _rule_iter; + LBData::InterfaceHealthIter _health_iter; + LBRule::InterfaceDistIter _rule_iface_iter; +}; + +#endif //__LBCONFLOADER_HH__ diff --git a/src/lbdecision.cc b/src/lbdecision.cc new file mode 100644 index 0000000..79e86cb --- /dev/null +++ b/src/lbdecision.cc @@ -0,0 +1,318 @@ +/* + * Module: lbdecision.cc + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#include <syslog.h> +#include <iostream> +#include "lbdata.hh" +#include "lbdecision.hh" + +using namespace std; + +/* +iptables -t mangle -N ISP1 +iptables -t mangle -A ISP1 -j CONNMARK --set-mark 1 +iptables -t mangle -A ISP1 -j MARK --set-mark 1 +iptables -t mangle -A ISP1 -j ACCEPT + +iptables -t mangle -N ISP2 +iptables -t mangle -A ISP2 -j CONNMARK --set-mark 2 +iptables -t mangle -A ISP2 -j MARK --set-mark 2 +iptables -t mangle -A ISP2 -j ACCEPT + + +#THIS APPEARS TO ROUGHLY WORK BELOW, AND CAN BE SET UP WITH SPECIFIC FILTERS. +iptables -t mangle -A PREROUTING -i eth0 -m statistic --mode nth --every 2 --packet 0 -j ISP1 +iptables -t mangle -A PREROUTING -i eth0 -j ISP2 + +#iptables -t mangle -A PREROUTING -i eth0 -m state --state NEW -m statistic --mode random --probability .01 -j MARK --set-mark 1 +#iptables -t mangle -A PREROUTING -i eth0 -j MARK --set-mark 2 + +iptables -t raw -N NAT_CONNTRACK +iptables -t raw -A NAT_CONNTRACK -j ACCEPT +iptables -t raw -I PREROUTING 1 -j NAT_CONNTRACK +iptables -t raw -I OUTPUT 1 -j NAT_CONNTRACK +ip ro add table 10 default via 192.168.1.2 dev eth1 +ip ru add fwmark 1 table 10 +ip ro fl ca +ip ro add table 20 default via 192.168.2.2 dev eth2 +ip ru add fwmark 2 table 20 +ip ro fl ca + +*/ + + +/** + * + * + **/ +LBDecision::LBDecision() +{ + +} + +/** + * + * + **/ +LBDecision::~LBDecision() +{ + shutdown(); +} + +/** + * + * + **/ +void +LBDecision::init(LBData &lbdata) +{ + //here is where we set up iptables and policy routing for the interfaces + /* + iptables -t mangle -N ISP1 + iptables -t mangle -A ISP1 -j CONNMARK --set-mark 1 + iptables -t mangle -A ISP1 -j MARK --set-mark 1 + iptables -t mangle -A ISP1 -j ACCEPT + */ + + char buf[20]; + int ct = 1; + + /* + do we need: +iptables -t raw -N NAT_CONNTRACK +iptables -t raw -A NAT_CONNTRACK -j ACCEPT +iptables -t raw -I PREROUTING 1 -j NAT_CONNTRACK +iptables -t raw -I OUTPUT 1 -j NAT_CONNTRACK + +if so then this stuff goes here! + */ + + + //note: doesn't appear to clean up rule table, may need to individually erase each rule + // execute(string("ip rule flush")); + + LBData::InterfaceHealthIter iter = lbdata._iface_health_coll.begin(); + while (iter != lbdata._iface_health_coll.end()) { + string iface = iter->first; + sprintf(buf,"%d",ct); + execute(string("iptables -t mangle -N ISP_") + buf); + execute(string("iptables -t mangle -F ISP_") + buf); + execute(string("iptables -t mangle -A ISP_") + buf + " -j CONNMARK --set-mark " + buf); + execute(string("iptables -t mangle -A ISP_") + buf + " -j MARK --set-mark " + buf); + + //NOTE, WILL NEED A WAY TO CLEAN UP THIS RULE ON RESTART... + execute(string("iptables -t mangle -A ISP_") + buf + " -j ACCEPT"); + + execute(string("ip route replace table ") + buf + " default dev " + iface); + execute(string("ip rule add fwmark ") + buf + " table " + buf); + + _iface_mark_coll.insert(pair<string,int>(iface,ct)); + ++ct; + ++iter; + } + execute("ip route flush cache"); +} + + +/** + * only responsible for + +iptables -t mangle -A PREROUTING -i eth0 -m state --state NEW -m statistic --mode random --probability .01 -j MARK --set-mark 1 +iptables -t mangle -A PREROUTING -i eth0 -j MARK --set-mark 2 + + * + * + * + **/ +void +LBDecision::run(LBData &lb_data) +{ +#ifdef DEBUG + cout << "LBDecision::run(), starting decision" << endl; +#endif + + //first determine if we need to alter the rule set + if (!lb_data.state_changed()) { + return; + } + +#ifdef DEBUG + cout << "LBDecision::run(), state changed, applying new rule set" << endl; +#endif + + //then if we do, flush all + execute("iptables -t mangle -F PREROUTING"); + + //and compute the new set and apply + LBData::LBRuleIter iter = lb_data._lb_rule_coll.begin(); + while (iter != lb_data._lb_rule_coll.end()) { + map<int,float> weights = get_new_weights(lb_data,iter->second); + map<int,float>::iterator w_iter = weights.begin(); + map<int,float>::iterator w_end = weights.end(); + if (w_iter == w_end) { + ++iter; + continue; + } + else { + --w_end; + } + + //NEED TO HANDLE APPLICATION SPECIFIC DETAILS + string app_cmd = get_application_cmd(iter->second); + + char fbuf[20],dbuf[20]; + while (w_iter != w_end) { + sprintf(fbuf,"%f",w_iter->second); + sprintf(dbuf,"%d",w_iter->first); + execute(string("iptables -t mangle -A PREROUTING ") + app_cmd + " -m state --state NEW -m statistic --mode random --probability " + fbuf + " -j ISP_" + dbuf); + ++w_iter; + } + //last one is special case, the catch all rule + ++w_iter; + sprintf(dbuf,"%d",w_iter->first); + execute(string("iptables -t mangle -A PREROUTING ") + app_cmd + " -j ISP_" + dbuf); + ++iter; + } +} + +/** + * + * + **/ +void +LBDecision::shutdown() +{ + char buf[20]; + + //then if we do, flush all + execute("iptables -t mangle -F PREROUTING"); + + //remove the policy entries + InterfaceMarkIter iter = _iface_mark_coll.begin(); + while (iter != _iface_mark_coll.end()) { + sprintf(buf,"%d",iter->second); + execute(string("ip rule del fwmark ") + buf); + ++iter; + } +} + +/** + * + * + **/ +void +LBDecision::execute(string cmd) +{ +#ifdef DEBUG + cout << "LBDecision::execute(): applying command to system: " << cmd << endl; +#endif + + FILE *f = popen(cmd.c_str(), "w"); + if (f) { + if (pclose(f) != 0) { + cerr << "LBDecision::execute(): error executing command: " << cmd << endl; + syslog(LOG_ERR, "Error executing system command: %s", cmd.c_str()); + } + } + else { + cerr << "LBDecision::execute(): error executing command: " << cmd << endl; + syslog(LOG_ERR, "Error executing system command: %s", cmd.c_str()); + } +} + +map<int,float> +LBDecision::get_new_weights(LBData &data, LBRule &rule) +{ + map<int,float> weights; + int group = 0; + int ct = 1; + LBRule::InterfaceDistIter iter = rule._iface_dist_coll.begin(); + while (iter != rule._iface_dist_coll.end()) { +#ifdef DEBUG + cout << "LBDecision::get_new_weights(): " << iter->first << " is active: " << (data.is_active(iter->first) ? "true" : "false") << endl; +#endif + if (data.is_active(iter->first)) { + weights.insert(pair<int,float>(ct,iter->second)); + group += iter->second; + } + ++ct; + ++iter; + } + + //now weight the overall distribution + map<int,float>::iterator w_iter = weights.begin(); + while (w_iter != weights.end()) { + float w = float(w_iter->second) / float(group); + group -= w_iter->second; //I THINK THIS NEEDS TO BE ADJUSTED TO THE OVERALL REMAINING VALUES. which is this... + w_iter->second = w; + ++w_iter; + } + + return weights; +} + +/** + * + * + **/ +string +LBDecision::get_application_cmd(LBRule &rule) +{ + string filter; + + if (rule._proto.empty() == false) { + filter += "--proto " + rule._proto + " "; + } + + if (rule._proto == "icmp") { + filter += "--icmp-type any "; + } + else if (rule._proto == "udp" || rule._proto == "tcp") { + if (rule._s_addr.empty() == false) { + filter += "--source " + rule._s_addr + " "; + } + else if (rule._s_net.empty() == false) { + filter += "--source " + rule._s_net + " "; + } + + if (rule._d_addr.empty() == false) { + filter += "--destination " + rule._d_addr + " "; + } + else if (rule._d_net.empty() == false) { + filter += "--destination " + rule._d_net + " "; + } + + if (rule._s_port_name.empty() == false) { + filter += "--source-port " + rule._s_port_name + " "; + } + else if (rule._s_port_num.empty() == false) { + filter += "--source-port " + rule._s_port_num + " "; + } + } + + return filter; +} diff --git a/src/lbdecision.hh b/src/lbdecision.hh new file mode 100644 index 0000000..299544a --- /dev/null +++ b/src/lbdecision.hh @@ -0,0 +1,70 @@ +/* + * Module: lbdecision.hh + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#ifndef __LBDECISION_HH__ +#define __LBDECISION_HH__ + +#include <map> +#include <string> +#include "lbdata.hh" + +using namespace std; + +class LBDecision +{ +public: + typedef map<string,int> InterfaceMarkColl; + typedef map<string,int>::iterator InterfaceMarkIter; + +public: + LBDecision(); + ~LBDecision(); + + void + init(LBData &lbdata); + + void + run(LBData &lbdata); + + void + shutdown(); + +private: + void + execute(string cmd); + + map<int,float> + get_new_weights(LBData &data, LBRule &rule); + + string + get_application_cmd(LBRule &rule); + +private: + InterfaceMarkColl _iface_mark_coll; +}; + +#endif //__LBDECISION_HH__ diff --git a/src/lboutput.cc b/src/lboutput.cc new file mode 100644 index 0000000..e0ed18b --- /dev/null +++ b/src/lboutput.cc @@ -0,0 +1,58 @@ +/* + * Module: lboutput.cc + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#include <sys/time.h> +#include <time.h> + +#include <iostream> + +#include "lbdata.hh" +#include "lboutput.hh" + +void +LBOutput::write(const LBData &lbdata) +{ + timeval tv; + gettimeofday(&tv,NULL); + + //dump out the health data + LBData::InterfaceHealthConstIter iter = lbdata._iface_health_coll.begin(); + while (iter != lbdata._iface_health_coll.end()) { + cout << iter->first << " "; //interface + cout << string(iter->second._is_active ? "true" : "false") << " "; //status + cout << tv.tv_sec - iter->second._last_success << " "; //last success + cout << tv.tv_sec - iter->second._last_failure << " "; //last failure + ++iter; + } + + //dump out the application data + LBData::LBRuleConstIter r_iter = lbdata._lb_rule_coll.begin(); + while (r_iter != lbdata._lb_rule_coll.end()) { + cout << "squirt out results here." << endl; + ++r_iter; + } +} diff --git a/src/lboutput.hh b/src/lboutput.hh new file mode 100644 index 0000000..b68c9f2 --- /dev/null +++ b/src/lboutput.hh @@ -0,0 +1,45 @@ +/* + * Module: lboutput.hh + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#ifndef __LBOUTPUT_HH__ +#define __LBOUTPUT_HH__ + +#include "lbdata.hh" + +using namespace std; + +class LBOutput +{ +public: + LBOutput() {} + ~LBOutput() {} + + void + write(const LBData &lbdata); + +}; +#endif //__LBOUTPUT_HH__ diff --git a/src/lbpathtest.cc b/src/lbpathtest.cc new file mode 100644 index 0000000..7e052c7 --- /dev/null +++ b/src/lbpathtest.cc @@ -0,0 +1,336 @@ +/* + * Module: lbpathtest.cc + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#include <syslog.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/sysinfo.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/udp.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <errno.h> +#include <memory> +#include <time.h> +#include <sys/timeb.h> +#include <stdio.h> +#include <stdlib.h> +#include <iostream> +#include <string> +#include <algorithm> + +#include "lbdata.hh" +#include "lbpathtest.hh" + +using namespace std; + +LBPathTest::LBPathTest() : + _send_sock(0), + _recv_sock(0), + _packet_id(0) +{ + struct protoent *ppe = getprotobyname("icmp"); + _send_sock = socket(PF_INET, SOCK_RAW, ppe->p_proto); + if (_send_sock < 0){ + cerr << "LBPathTest::LBPathTest(): no send sock: " << _send_sock << endl; + syslog(LOG_ERR, "wan_lb: failed to acquired socket"); + _send_sock = 0; + return; + } + + //set options for broadcasting. + int val = 1; + setsockopt(_send_sock, SOL_SOCKET, SO_BROADCAST, &val, 4); + setsockopt(_send_sock, SOL_SOCKET, SO_REUSEADDR, &val, 4); + + struct sockaddr_in addr; + memset( &addr, 0, sizeof( struct sockaddr_in )); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = 0; + + _recv_sock = socket(PF_INET, SOCK_RAW, ppe->p_proto); + if (_recv_sock < 0) { + cerr << "LBPathTest::LBPathTest(): no recv sock: " << _recv_sock << endl; + syslog(LOG_ERR, "wan_lb: failed to acquired socket"); + _recv_sock = 0; + return; + } + if (bind(_recv_sock, (struct sockaddr*)&addr, sizeof(addr))==-1) { + cerr << "failed on bind" << endl; + syslog(LOG_ERR, "wan_lb: failed to bind recv sock"); + } +} + +LBPathTest::~LBPathTest() +{ + if (_recv_sock) + close(_recv_sock); + + if (_send_sock) + close(_send_sock); +} + +void +LBPathTest::start(LBData &lb_data) +{ +#ifdef DEBUG + cout << "LBPathTest::start(): starting health test. client ct: " << lb_data._iface_health_coll.size() << endl; +#endif + + map<int,PktData> results; + + struct timeval send_time; + gettimeofday(&send_time,NULL); + + int ct = 0; + //iterate over packets and send + LBData::InterfaceHealthIter iter = lb_data._iface_health_coll.begin(); + while (iter != lb_data._iface_health_coll.end()) { +#ifdef DEBUG + cout << "LBPathTest::start(): sending ping test for: " << iter->first << " for " << iter->second._ping_target << endl; +#endif + _packet_id = ++_packet_id % 32767; + send(iter->first, iter->second._ping_target, _packet_id); + results.insert(pair<int,PktData>(_packet_id,PktData(iter->first,-1))); + + ++ct; + ++iter; + } + + //use gettimeofday to calculate time to millisecond + + //use sysinfo to make sure we don't get stuck in a loop with timechange + struct sysinfo si; + sysinfo(&si); + //for now hardcode to 5 second overall timeout + int timeout = si.uptime + 5; //seconds + int cur_time = si.uptime; + + //then iterate over recv socket and receive and record + while (ct > 0 && cur_time < timeout) { + int id = receive(); +#ifdef DEBUG + cout << "LBPathTest::start(): " << id << endl; +#endif + //update current time for comparison + sysinfo(&si); + timeval recv_time; + gettimeofday(&recv_time,NULL); + cur_time = si.uptime; + map<int,PktData>::iterator r_iter = results.find(id); + if (r_iter != results.end()) { + + //calculate time in milliseconds + int secs = 0; + int msecs = recv_time.tv_usec - send_time.tv_usec; + if (msecs < 0) { + secs = recv_time.tv_sec - send_time.tv_sec - 1; + } + else { + secs = recv_time.tv_sec - send_time.tv_sec; + } + //time in milliseconds below + int rtt = abs(msecs) / 1000 + 1000 * secs; + + LBData::InterfaceHealthIter iter = lb_data._iface_health_coll.find(r_iter->second._iface); + if (iter != lb_data._iface_health_coll.end()) { + //check to see if this returned in the configured time, otherwise apply timeout +#ifdef DEBUG + cout << "LBPathTest::start(): received pkt: " << iter->first << ", rtt: " << rtt << endl; +#endif + if (rtt < iter->second._ping_resp_time) { + iter->second.put(rtt); + } + else { + iter->second.put(-1); + } + } + results.erase(r_iter); + --ct; + } + } + + //we're done waiting, mark the rest as non-responsive + map<int,PktData>::iterator r_iter = results.begin(); + while (r_iter != results.end()) { + LBData::InterfaceHealthIter iter = lb_data._iface_health_coll.find(r_iter->second._iface); + if (iter != lb_data._iface_health_coll.end()) { + iter->second.put(-1); + } + ++r_iter; + } + +#ifdef DEBUG + cout << "LBPathTest::start(): finished heath test" << endl; +#endif +} + +void +LBPathTest::send(const string &iface, const string &target_addr, int packet_id) +{ + int err; + sockaddr_in taddr; + timeval send_time; + icmphdr *icmp_hdr; + int icmp_pktsize = 40; + char buffer[icmp_pktsize]; + + // bind a socket to a device name (might not work on all systems): + setsockopt(_send_sock, SOL_SOCKET, SO_BINDTODEVICE, iface.c_str(), iface.size()); + + //convert target_addr to ip addr + struct hostent *h = gethostbyname(target_addr.c_str()); + if (h == NULL) { + cerr << "LBPathTest::send() Error in resolving hostname" << endl; + syslog(LOG_ERR, "wan_lb: error in resolving configured hostname: %s", target_addr.c_str()); + return; + } + + icmp_hdr = (struct icmphdr *)buffer; + icmp_hdr->type = ICMP_ECHO; + icmp_hdr->code = 0; + icmp_hdr->checksum = 0; + icmp_hdr->un.echo.id = htons(getpid()); + icmp_hdr->un.echo.sequence = 0; + int length = sizeof(buffer); + + //we'll put in time of packet sent for the heck of it, may + //want to use this in future tests, feel free to remove. + gettimeofday(&send_time, (struct timezone*)NULL); + char* datap = &buffer[8]; + memcpy(datap, (char*)&send_time.tv_sec, sizeof(send_time.tv_sec)); + datap = &buffer[12]; + memcpy(datap, (char*)&send_time.tv_usec, sizeof(send_time.tv_usec)); + datap = &buffer[16]; + memcpy(datap, (char*)&packet_id, sizeof(packet_id)); //packet id + datap = &buffer[18]; + int val(icmp_pktsize); + memcpy(datap, (char*)&val, 2); //packet id + + icmp_hdr->un.echo.sequence = 1; + icmp_hdr->checksum = 0; + icmp_hdr->checksum = in_checksum((unsigned short *)icmp_hdr,length>>1); + + struct in_addr ia; + memcpy(&ia, h->h_addr_list[0], sizeof(ia)); + unsigned long addr = ia.s_addr; + + taddr.sin_addr.s_addr = addr; + taddr.sin_family = AF_INET; + bzero(&(taddr.sin_zero), 8); + + //need to direct this packet out a specific interface!!!!!!!!!!!!! + err = sendto(_send_sock, buffer, icmp_pktsize, 0, (struct sockaddr*)&taddr, sizeof(taddr)); +#ifdef DEBUG + cout << "lbpathtest: sendto: " << err << ", packet id: " << packet_id << endl; +#endif + if(err < 0) + { + if (errno == EBADF) + cout << "EBADF" << endl; + else if (errno == ENOTSOCK) + cout << "ENOTSOCK" << endl; + else if (errno == EFAULT) + cout << "EFAULT" << endl; + else if (errno == EMSGSIZE) + cout << "EMSGSIZE" << endl; + else if (errno == EWOULDBLOCK) + cout << "EWOULDBLOCK" << endl; + else if (errno == EAGAIN) + cout << "EAGAIN" << endl; + else if (errno == ENOBUFS) + cout << "ENOBUFS" << endl; + else if (errno == EINTR) + cout << "EINTR" << endl; + else if (errno == ENOMEM) + cout << "ENOMEM" << endl; + else if (errno == EACCES) + cout << "EACCES" << endl; + else if (errno == EINVAL) + cout << "EINVAL" << endl; + else if (errno == EPIPE) + cout << "EPIPE" << endl; + else + cout << "unknown error: " << errno << endl; + + syslog(LOG_ERR, "wan_lb: error on sending icmp packet: %d", errno); + } +} + +int +LBPathTest::receive() +{ + int icmp_pktsize = 40; + char resp_buf[icmp_pktsize]; + icmphdr *icmp_hdr; + timeval wait_time; + fd_set readfs; + int ret; + + FD_ZERO(&readfs); + FD_SET(_recv_sock, &readfs); + + wait_time.tv_usec = 0; + wait_time.tv_sec = 3; //3 second timeout + + while (select(_recv_sock+1, &readfs, NULL, NULL, &wait_time) != 0) + { + ret = recv(_recv_sock, &resp_buf, icmp_pktsize, 0); + if (ret != -1) + { + icmp_hdr = (struct icmphdr *)(resp_buf + sizeof(iphdr)); + if (icmp_hdr->type == ICMP_ECHOREPLY) + { +#ifdef DEBUG + cout << "LBPathTest::receive(): " << endl; +#endif + //process packet data + char* data; + int id = 0; + data = (char*)(&resp_buf) + 36; + memcpy(&id, data, sizeof(unsigned short)); + return id; + } + } + } + return -1; +} + +unsigned short +LBPathTest::in_checksum(const unsigned short *buffer, int length) const +{ + unsigned long sum; + for (sum=0; length>0; length--) + sum += *buffer++; + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + return ~sum; +} + diff --git a/src/lbpathtest.hh b/src/lbpathtest.hh new file mode 100644 index 0000000..176bf18 --- /dev/null +++ b/src/lbpathtest.hh @@ -0,0 +1,73 @@ +/* + * Module: lbpathtest.hh + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#ifndef __LBPATHTEST_HH__ +#define __LBPATHTEST_HH__ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <iostream> +#include "lbdata.hh" + +using namespace std; + +class PktData +{ +public: + PktData(string iface, int rtt) : _iface(iface),_rtt(rtt) {} + string _iface; + int _rtt; +}; + +class LBPathTest +{ +public: + LBPathTest(); + ~LBPathTest(); + + void + start(LBData &lb_data); + +private: + void + send(const string &iface, const string &target_addr, int packet_id); + + int + receive(); + + unsigned short + in_checksum(const unsigned short *buf, int lenght) const; + +private: + int _send_sock; + int _recv_sock; + int _packet_id; +}; + + +#endif //__LBPATHTEST_HH__ diff --git a/src/lbrule.hh b/src/lbrule.hh new file mode 100644 index 0000000..b4d2683 --- /dev/null +++ b/src/lbrule.hh @@ -0,0 +1,5 @@ +#ifndef __LBRULE_HH__ +#define __LBRULE_HH__ + + +#endif //__LBRULE_HH__ diff --git a/src/loadbalance.cc b/src/loadbalance.cc new file mode 100644 index 0000000..a48ace0 --- /dev/null +++ b/src/loadbalance.cc @@ -0,0 +1,110 @@ +/* + * Module: loadbalance.cc + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#include <string> +#include "lbpathtest.hh" +#include "loadbalance.hh" + +using namespace std; + +/** + * + **/ +LoadBalance::LoadBalance() : + _cycle_interval(5000) +{ +} + +/** + * + **/ +LoadBalance::~LoadBalance() +{ + _decision.shutdown(); +} + +/** + * + **/ +bool +LoadBalance::set_conf(const string &conf) +{ + _lbdata_factory.load(conf); + _lbdata = _lbdata_factory.get(); + if (_lbdata.error()) { + return false; + } + + return true; +} + +/** + * + **/ +void +LoadBalance::init() +{ + _decision.init(_lbdata); +} + +/** + * + **/ +bool +LoadBalance::start_cycle() +{ + _lbdata.reset_state_changed(); + return true; +} + +/** + * + **/ +void +LoadBalance::health_test() +{ + _ph.start(_lbdata); +} + +/** + * + **/ +void +LoadBalance::apply_rules() +{ + _decision.run(_lbdata); +} + +/** + * + **/ +void +LoadBalance::output() +{ + _output.write(_lbdata); +} + diff --git a/src/loadbalance.conf b/src/loadbalance.conf new file mode 100644 index 0000000..85e6c5d --- /dev/null +++ b/src/loadbalance.conf @@ -0,0 +1,50 @@ +# +# Sample vyatta load balance configuration file. +# + +health { + interface eth1 { + target 10.0.0.1 + success-ct 2 + failure-ct 1 + ping-resp 100 + } + + interface eth2 { + target 10.0.0.1 + success-ct 1 + failure-ct 1 + ping-resp 1000 + } +} + +rule 1 { + protocol udp + interface eth1 { + weight 1 + } + interface eth2 { + weight 2 + } +} + +rule 2 { + protocol tcp + source { + address 1.1.1.1 + port-number 2222 + } + interface eth1 { + weight 1 + } + interface eth2 { + weight 3 + } +} + +#default rule w/o protocol specified +rule 10 { + interface eth2 { + weight 1 + } +}
\ No newline at end of file diff --git a/src/loadbalance.hh b/src/loadbalance.hh new file mode 100644 index 0000000..624a3e4 --- /dev/null +++ b/src/loadbalance.hh @@ -0,0 +1,75 @@ +/* + * Module: loadbalance.hh + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#ifndef __LOADBALANCE_HH__ +#define __LOADBALANCE_HH__ + +#include <time.h> + +#include <string> +#include <map> + +#include "lbdatafactory.hh" +#include "lbpathtest.hh" +#include "lbdecision.hh" +#include "lboutput.hh" +#include "lbdata.hh" + + +using namespace std; + +class LoadBalance +{ + public: + LoadBalance(); + ~LoadBalance(); + + bool set_conf(const string &filename); + + void init(); + + bool start_cycle(); + + void health_test(); + + void apply_rules(); + + void output(); + + //temporary stand-in for now... + void sleep() {::sleep(5);} + + private: + LBDataFactory _lbdata_factory; + LBData _lbdata; + LBPathTest _ph; + LBDecision _decision; + LBOutput _output; + int _cycle_interval; +}; + +#endif //__LOADBALANCE_HH__ diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..e8d93e0 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,138 @@ +/* + * Module: main.cc + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2007 + * Description: + * + * **** End License **** + * + */ +#include <signal.h> +#include <syslog.h> +#include <stdio.h> +#include <iostream> +#include "loadbalance.hh" + +LoadBalance *g_lb = NULL; + + +static void usage() +{ + cout << "lb -fh" << endl; + cout << "-f [file] configuration file" << endl; + cout << "-t load configuration file only and exit" << endl; + cout << "-h help" << endl; + +} + +static void sig_end(int signo) +{ + if (g_lb) + delete g_lb; + cerr << "End signal: " << signo << endl; + syslog(LOG_ERR, "wan_lb, exit signal caught, exiting.."); + exit(0); +} + +static void sig_user(int signo) +{ + if (g_lb) + delete g_lb; + cerr << "User signal: " << signo << endl; + syslog(LOG_ERR, "wan_lb, user exit signal caught, exiting.."); + exit(0); +} + + +int main(int argc, char* argv[]) +{ + int ch; + bool config_debug_mode = false; + string c_file; + + //grab inputs + while ((ch = getopt(argc, argv, "f:ht")) != -1) { + switch (ch) { + case 'f': + c_file = optarg; + break; + case 'h': + usage(); + exit(0); + case 't': + config_debug_mode = true; + break; + default: + usage(); + exit(0); + } + } + + //parse conf file + if (c_file.empty()) { + cout << "Configuration file is empty" << endl; + exit(0); + } + + g_lb = new LoadBalance(); + + bool success = g_lb->set_conf(c_file); + if (success == false) { + syslog(LOG_ERR, "wan_lb: error loading configuration file: %s", c_file.c_str()); + exit(0); + } + + if (config_debug_mode) { + exit(0); + } + +#ifdef DEBUG + cout << "STARTING CYCLE" << endl; +#endif + + g_lb->init(); + + + //signal handler here + // sighup... + signal(SIGINT, sig_end); + signal(SIGTERM, sig_end); + signal(SIGUSR1, sig_user); + + //drop into event loop + do { +#ifdef DEBUG + cout << "main.cc: starting new cycle" << endl; +#endif + + //health test + g_lb->health_test(); + + //apply rules + g_lb->apply_rules(); + + //update show output + g_lb->output(); + + g_lb->sleep(); + } while (g_lb->start_cycle()); + exit(0); +} diff --git a/src/rl_str_proc.cc b/src/rl_str_proc.cc new file mode 100644 index 0000000..3a5d151 --- /dev/null +++ b/src/rl_str_proc.cc @@ -0,0 +1,82 @@ +#include "rl_str_proc.hh" + +using namespace std; + +/** + * + **/ +StrProc::StrProc(const string &in_str, const string &token) +{ + string tmp = in_str; + + //convert tabs to spaces + uint32_t pos = 0; + string tabtospace = " "; + string::iterator iter = tmp.begin(); + while ((pos = tmp.find("\t", pos)) != string::npos) { + tmp.replace(pos, 1, tabtospace); + pos += tabtospace.length(); + } + + //remove the cr + pos = tmp.find("\n"); + if (pos != string::npos) { + tmp.replace(pos, 1, ""); + } + + //now handle the case of the multiple length token + //note that we are using the '~' as a token internally + uint32_t start = 0, end; + while ((start = tmp.find(token, start)) != string::npos) { + tmp.replace(start, token.length(), "~"); + } + + + while ((start = tmp.find_first_not_of("~")) != string::npos) { + tmp = tmp.substr(start, tmp.length() - start); + end = tmp.find_first_of("~"); + _str_coll.push_back(tmp.substr(0, end)); + tmp = tmp.substr(end+1, tmp.length() - end-1); + if (end == string::npos) { + break; + } + } +} + +/** + * + **/ +string +StrProc::get(int i) +{ + if (uint32_t(i) >= _str_coll.size()) { + return string(""); + } + return _str_coll[i]; +} + +/** + * + **/ +string +StrProc::get(int start, int end) +{ + if (uint32_t(start) >= _str_coll.size()) { + return string(""); + } + + string tmp; + for (int i = start; (i < end) && (uint32_t(i) < _str_coll.size()); ++i) { + tmp += _str_coll[i] + " "; + } + return tmp.substr(0,tmp.length()-1); +} + +/** + * + **/ +vector<string> +StrProc::get() +{ + return _str_coll; +} diff --git a/src/rl_str_proc.hh b/src/rl_str_proc.hh new file mode 100644 index 0000000..c59df0a --- /dev/null +++ b/src/rl_str_proc.hh @@ -0,0 +1,24 @@ +#ifndef __RL_STR_PROC_HH__ +#define __RL_STR_PROC_HH__ + +#include <vector> +#include <string> + +class StrProc +{ +public: + StrProc(const std::string &in, const std::string &token); + + std::string get(int i); + + std::string get(int start, int end); + + std::vector<std::string> get(); + + int size() {return _str_coll.size();} + +private: + std::vector<std::string> _str_coll; +}; + +#endif //__RL_STR_PROC_HH__ |