summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in6
-rw-r--r--aclocal.m4480
-rw-r--r--conf/cf-lex.l14
-rw-r--r--configure.ac9
-rw-r--r--filter/config.Y10
-rw-r--r--filter/filter.c26
-rw-r--r--filter/filter.h17
-rw-r--r--lib/printf.c1
-rw-r--r--lua/Makefile4
-rw-r--r--lua/common.c323
-rw-r--r--lua/filter.c187
-rw-r--r--lua/lua.h18
-rw-r--r--nest/config.Y3
-rw-r--r--nest/proto.c3
-rw-r--r--nest/protocol.h4
-rw-r--r--nest/route.h13
-rw-r--r--proto/wireguard/Makefile8
-rw-r--r--proto/wireguard/config.Y48
-rw-r--r--proto/wireguard/wireguard.c222
-rw-r--r--proto/wireguard/wireguard.h22
20 files changed, 1406 insertions, 12 deletions
diff --git a/Makefile.in b/Makefile.in
index 0ecd6811..b858e898 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -7,12 +7,12 @@ MAKEFLAGS += -r
# Variable definitions
CPPFLAGS=-I$(objdir) -I$(srcdir) @CPPFLAGS@
-CFLAGS=$(CPPFLAGS) @CFLAGS@
+CFLAGS=$(CPPFLAGS) @CFLAGS@ @LUA_INCLUDE@
LDFLAGS=@LDFLAGS@
M4FLAGS=@M4FLAGS@
BISONFLAGS=@BISONFLAGS@
LIBS=@LIBS@
-DAEMON_LIBS=@DAEMON_LIBS@
+DAEMON_LIBS=@DAEMON_LIBS@ @LUA_LIB@
CLIENT_LIBS=@CLIENT_LIBS@
CC=@CC@
M4=@M4@
@@ -74,7 +74,7 @@ cli: $(client)
$(daemon): LIBS += $(DAEMON_LIBS)
# Include directories
-dirs := client conf doc filter lib nest test $(addprefix proto/,$(protocols)) @sysdep_dirs@
+dirs := client conf doc filter lib lua nest test $(addprefix proto/,$(protocols)) @sysdep_dirs@
conf-y-targets := $(addprefix $(objdir)/conf/,cf-parse.y keywords.h commands.h)
cf-local = $(conf-y-targets): $(s)config.Y
diff --git a/aclocal.m4 b/aclocal.m4
index c401d447..7fc7f67e 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -196,3 +196,483 @@ AC_DEFUN([BIRD_CHECK_BISON_VERSION],
;;
esac
])
+
+dnl =========================================================================
+dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl =========================================================================
+AC_DEFUN([AX_PROG_LUA],
+[
+ dnl Check for required tools.
+ AC_REQUIRE([AC_PROG_GREP])
+ AC_REQUIRE([AC_PROG_SED])
+
+ dnl Make LUA a precious variable.
+ AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
+
+ dnl Find a Lua interpreter.
+ m4_define_default([_AX_LUA_INTERPRETER_LIST],
+ [lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50])
+
+ m4_if([$1], [],
+ [ dnl No version check is needed. Find any Lua interpreter.
+ AS_IF([test "x$LUA" = 'x'],
+ [AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])])
+ ax_display_LUA='lua'
+
+ AS_IF([test "x$LUA" != 'x:'],
+ [ dnl At least check if this is a Lua interpreter.
+ AC_MSG_CHECKING([if $LUA is a Lua interpreter])
+ _AX_LUA_CHK_IS_INTRP([$LUA],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([not a Lua interpreter])
+ ])
+ ])
+ ],
+ [ dnl A version check is needed.
+ AS_IF([test "x$LUA" != 'x'],
+ [ dnl Check if this is a Lua interpreter.
+ AC_MSG_CHECKING([if $LUA is a Lua interpreter])
+ _AX_LUA_CHK_IS_INTRP([$LUA],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([not a Lua interpreter])
+ ])
+ dnl Check the version.
+ m4_if([$2], [],
+ [_ax_check_text="whether $LUA version >= $1"],
+ [_ax_check_text="whether $LUA version >= $1, < $2"])
+ AC_MSG_CHECKING([$_ax_check_text])
+ _AX_LUA_CHK_VER([$LUA], [$1], [$2],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([version is out of range for specified LUA])])
+ ax_display_LUA=$LUA
+ ],
+ [ dnl Try each interpreter until we find one that satisfies VERSION.
+ m4_if([$2], [],
+ [_ax_check_text="for a Lua interpreter with version >= $1"],
+ [_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
+ AC_CACHE_CHECK([$_ax_check_text],
+ [ax_cv_pathless_LUA],
+ [ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do
+ test "x$ax_cv_pathless_LUA" = 'xnone' && break
+ _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
+ _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
+ done
+ ])
+ dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA.
+ AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'],
+ [LUA=':'],
+ [AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])])
+ ax_display_LUA=$ax_cv_pathless_LUA
+ ])
+ ])
+
+ AS_IF([test "x$LUA" = 'x:'],
+ [ dnl Run any user-specified action, or abort.
+ m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])])
+ ],
+ [ dnl Query Lua for its version number.
+ AC_CACHE_CHECK([for $ax_display_LUA version],
+ [ax_cv_lua_version],
+ [ dnl Get the interpreter version in X.Y format. This should work for
+ dnl interpreters version 5.0 and beyond.
+ ax_cv_lua_version=[`$LUA -e '
+ -- return a version number in X.Y format
+ local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
+ print(ver)'`]
+ ])
+ AS_IF([test "x$ax_cv_lua_version" = 'x'],
+ [AC_MSG_ERROR([invalid Lua version number])])
+ AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
+ AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`])
+
+ dnl The following check is not supported:
+ dnl At times (like when building shared libraries) you may want to know
+ dnl which OS platform Lua thinks this is.
+ AC_CACHE_CHECK([for $ax_display_LUA platform],
+ [ax_cv_lua_platform],
+ [ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]])
+ AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform])
+
+ dnl Use the values of $prefix and $exec_prefix for the corresponding
+ dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct
+ dnl variables so they can be overridden if need be. However, the general
+ dnl consensus is that you shouldn't need this ability.
+ AC_SUBST([LUA_PREFIX], ['${prefix}'])
+ AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}'])
+
+ dnl Lua provides no way to query the script directory, and instead
+ dnl provides LUA_PATH. However, we should be able to make a safe educated
+ dnl guess. If the built-in search path contains a directory which is
+ dnl prefixed by $prefix, then we can store scripts there. The first
+ dnl matching path will be used.
+ AC_CACHE_CHECK([for $ax_display_LUA script directory],
+ [ax_cv_lua_luadir],
+ [ AS_IF([test "x$prefix" = 'xNONE'],
+ [ax_lua_prefix=$ac_default_prefix],
+ [ax_lua_prefix=$prefix])
+
+ dnl Initialize to the default path.
+ ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION"
+
+ dnl Try to find a path with the prefix.
+ _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script])
+ AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
+ [ dnl Fix the prefix.
+ _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'`
+ ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \
+ $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"`
+ ])
+ ])
+ AC_SUBST([luadir], [$ax_cv_lua_luadir])
+ AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE])
+
+ dnl Lua provides no way to query the module directory, and instead
+ dnl provides LUA_PATH. However, we should be able to make a safe educated
+ dnl guess. If the built-in search path contains a directory which is
+ dnl prefixed by $exec_prefix, then we can store modules there. The first
+ dnl matching path will be used.
+ AC_CACHE_CHECK([for $ax_display_LUA module directory],
+ [ax_cv_lua_luaexecdir],
+ [ AS_IF([test "x$exec_prefix" = 'xNONE'],
+ [ax_lua_exec_prefix=$ax_lua_prefix],
+ [ax_lua_exec_prefix=$exec_prefix])
+
+ dnl Initialize to the default path.
+ ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION"
+
+ dnl Try to find a path with the prefix.
+ _AX_LUA_FND_PRFX_PTH([$LUA],
+ [$ax_lua_exec_prefix], [module])
+ AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
+ [ dnl Fix the prefix.
+ _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'`
+ ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \
+ $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"`
+ ])
+ ])
+ AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir])
+ AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE])
+
+ dnl Run any user specified action.
+ $3
+ ])
+])
+
+dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA.
+AC_DEFUN([AX_WITH_LUA],
+[
+ AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]])
+ AX_PROG_LUA
+])
+
+
+dnl =========================================================================
+dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+dnl =========================================================================
+AC_DEFUN([_AX_LUA_CHK_IS_INTRP],
+[
+ dnl A minimal Lua factorial to prove this is an interpreter. This should work
+ dnl for Lua interpreters version 5.0 and beyond.
+ _ax_lua_factorial=[`$1 2>/dev/null -e '
+ -- a simple factorial
+ function fact (n)
+ if n == 0 then
+ return 1
+ else
+ return n * fact(n-1)
+ end
+ end
+ print("fact(5) is " .. fact(5))'`]
+ AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'],
+ [$2], [$3])
+])
+
+
+dnl =========================================================================
+dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION],
+dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE])
+dnl =========================================================================
+AC_DEFUN([_AX_LUA_CHK_VER],
+[
+ dnl Check that the Lua version is within the bounds. Only the major and minor
+ dnl version numbers are considered. This should work for Lua interpreters
+ dnl version 5.0 and beyond.
+ _ax_lua_good_version=[`$1 -e '
+ -- a script to compare versions
+ function verstr2num(verstr)
+ local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)")
+ if majorver and minorver then
+ return tonumber(majorver) * 100 + tonumber(minorver)
+ end
+ end
+ local minver = verstr2num("$2")
+ local _, _, trimver = string.find(_VERSION, "^Lua (.*)")
+ local ver = verstr2num(trimver)
+ local maxver = verstr2num("$3") or 1e9
+ if minver <= ver and ver < maxver then
+ print("yes")
+ else
+ print("no")
+ end'`]
+ AS_IF([test "x$_ax_lua_good_version" = "xyes"],
+ [$4], [$5])
+])
+
+
+dnl =========================================================================
+dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR)
+dnl =========================================================================
+AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
+[
+ dnl Get the script or module directory by querying the Lua interpreter,
+ dnl filtering on the given prefix, and selecting the shallowest path. If no
+ dnl path is found matching the prefix, the result will be an empty string.
+ dnl The third argument determines the type of search, it can be 'script' or
+ dnl 'module'. Supplying 'script' will perform the search with package.path
+ dnl and LUA_PATH, and supplying 'module' will search with package.cpath and
+ dnl LUA_CPATH. This is done for compatibility with Lua 5.0.
+
+ ax_lua_prefixed_path=[`$1 -e '
+ -- get the path based on search type
+ local searchtype = "$3"
+ local paths = ""
+ if searchtype == "script" then
+ paths = (package and package.path) or LUA_PATH
+ elseif searchtype == "module" then
+ paths = (package and package.cpath) or LUA_CPATH
+ end
+ -- search for the prefix
+ local prefix = "'$2'"
+ local minpath = ""
+ local mindepth = 1e9
+ string.gsub(paths, "(@<:@^;@:>@+)",
+ function (path)
+ path = string.gsub(path, "%?.*$", "")
+ path = string.gsub(path, "/@<:@^/@:>@*$", "")
+ if string.find(path, prefix) then
+ local depth = string.len(string.gsub(path, "@<:@^/@:>@", ""))
+ if depth < mindepth then
+ minpath = path
+ mindepth = depth
+ end
+ end
+ end)
+ print(minpath)'`]
+])
+
+
+dnl =========================================================================
+dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl =========================================================================
+AC_DEFUN([AX_LUA_HEADERS],
+[
+ dnl Check for LUA_VERSION.
+ AC_MSG_CHECKING([if LUA_VERSION is defined])
+ AS_IF([test "x$LUA_VERSION" != 'x'],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
+ ])
+
+ dnl Make LUA_INCLUDE a precious variable.
+ AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
+
+ dnl Some default directories to search.
+ LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'`
+ m4_define_default([_AX_LUA_INCLUDE_LIST],
+ [ /usr/include/lua$LUA_VERSION \
+ /usr/include/lua-$LUA_VERSION \
+ /usr/include/lua/$LUA_VERSION \
+ /usr/include/lua$LUA_SHORT_VERSION \
+ /usr/local/include/lua$LUA_VERSION \
+ /usr/local/include/lua-$LUA_VERSION \
+ /usr/local/include/lua/$LUA_VERSION \
+ /usr/local/include/lua$LUA_SHORT_VERSION \
+ ])
+
+ dnl Try to find the headers.
+ _ax_lua_saved_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
+ AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
+ CPPFLAGS=$_ax_lua_saved_cppflags
+
+ dnl Try some other directories if LUA_INCLUDE was not set.
+ AS_IF([test "x$LUA_INCLUDE" = 'x' &&
+ test "x$ac_cv_header_lua_h" != 'xyes'],
+ [ dnl Try some common include paths.
+ for _ax_include_path in _AX_LUA_INCLUDE_LIST; do
+ test ! -d "$_ax_include_path" && continue
+
+ AC_MSG_CHECKING([for Lua headers in])
+ AC_MSG_RESULT([$_ax_include_path])
+
+ AS_UNSET([ac_cv_header_lua_h])
+ AS_UNSET([ac_cv_header_lualib_h])
+ AS_UNSET([ac_cv_header_lauxlib_h])
+ AS_UNSET([ac_cv_header_luaconf_h])
+
+ _ax_lua_saved_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
+ AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
+ CPPFLAGS=$_ax_lua_saved_cppflags
+
+ AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
+ [ LUA_INCLUDE="-I$_ax_include_path"
+ break
+ ])
+ done
+ ])
+
+ AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
+ [ dnl Make a program to print LUA_VERSION defined in the header.
+ dnl TODO It would be really nice if we could do this without compiling a
+ dnl program, then it would work when cross compiling. But I'm not sure how
+ dnl to do this reliably. For now, assume versions match when cross compiling.
+
+ AS_IF([test "x$cross_compiling" != 'xyes'],
+ [ AC_CACHE_CHECK([for Lua header version],
+ [ax_cv_lua_header_version],
+ [ _ax_lua_saved_cppflags=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
+ AC_RUN_IFELSE(
+ [ AC_LANG_SOURCE([[
+#include <lua.h>
+#include <stdlib.h>
+#include <stdio.h>
+int main(int argc, char ** argv)
+{
+ if(argc > 1) printf("%s", LUA_VERSION);
+ exit(EXIT_SUCCESS);
+}
+]])
+ ],
+ [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \
+ $SED -n "s|^Lua \(@<:@0-9@:>@\{1,\}\.@<:@0-9@:>@\{1,\}\).\{0,\}|\1|p"`
+ ],
+ [ax_cv_lua_header_version='unknown'])
+ CPPFLAGS=$_ax_lua_saved_cppflags
+ ])
+
+ dnl Compare this to the previously found LUA_VERSION.
+ AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION])
+ AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"],
+ [ AC_MSG_RESULT([yes])
+ ax_header_version_match='yes'
+ ],
+ [ AC_MSG_RESULT([no])
+ ax_header_version_match='no'
+ ])
+ ],
+ [ AC_MSG_WARN([cross compiling so assuming header version number matches])
+ ax_header_version_match='yes'
+ ])
+ ])
+
+ dnl Was LUA_INCLUDE specified?
+ AS_IF([test "x$ax_header_version_match" != 'xyes' &&
+ test "x$LUA_INCLUDE" != 'x'],
+ [AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])])
+
+ dnl Test the final result and run user code.
+ AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1],
+ [m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])])
+])
+
+dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS.
+AC_DEFUN([AX_LUA_HEADERS_VERSION],
+[
+ AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]])
+])
+
+
+dnl =========================================================================
+dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl =========================================================================
+AC_DEFUN([AX_LUA_LIBS],
+[
+ dnl TODO Should this macro also check various -L flags?
+
+ dnl Check for LUA_VERSION.
+ AC_MSG_CHECKING([if LUA_VERSION is defined])
+ AS_IF([test "x$LUA_VERSION" != 'x'],
+ [AC_MSG_RESULT([yes])],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION])
+ ])
+
+ dnl Make LUA_LIB a precious variable.
+ AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1])
+
+ AS_IF([test "x$LUA_LIB" != 'x'],
+ [ dnl Check that LUA_LIBS works.
+ _ax_lua_saved_libs=$LIBS
+ LIBS="$LIBS $LUA_LIB"
+ AC_SEARCH_LIBS([lua_load], [],
+ [_ax_found_lua_libs='yes'],
+ [_ax_found_lua_libs='no'])
+ LIBS=$_ax_lua_saved_libs
+
+ dnl Check the result.
+ AS_IF([test "x$_ax_found_lua_libs" != 'xyes'],
+ [AC_MSG_ERROR([cannot find libs for specified LUA_LIB])])
+ ],
+ [ dnl First search for extra libs.
+ _ax_lua_extra_libs=''
+
+ _ax_lua_saved_libs=$LIBS
+ LIBS="$LIBS $LUA_LIB"
+ AC_SEARCH_LIBS([exp], [m])
+ AC_SEARCH_LIBS([dlopen], [dl])
+ LIBS=$_ax_lua_saved_libs
+
+ AS_IF([test "x$ac_cv_search_exp" != 'xno' &&
+ test "x$ac_cv_search_exp" != 'xnone required'],
+ [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"])
+
+ AS_IF([test "x$ac_cv_search_dlopen" != 'xno' &&
+ test "x$ac_cv_search_dlopen" != 'xnone required'],
+ [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"])
+
+ dnl Try to find the Lua libs.
+ _ax_lua_saved_libs=$LIBS
+ LIBS="$LIBS $LUA_LIB"
+ AC_SEARCH_LIBS([lua_load],
+ [ lua$LUA_VERSION \
+ lua$LUA_SHORT_VERSION \
+ lua-$LUA_VERSION \
+ lua-$LUA_SHORT_VERSION \
+ lua \
+ ],
+ [_ax_found_lua_libs='yes'],
+ [_ax_found_lua_libs='no'],
+ [$_ax_lua_extra_libs])
+ LIBS=$_ax_lua_saved_libs
+
+ AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
+ test "x$ac_cv_search_lua_load" != 'xnone required'],
+ [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"])
+ ])
+
+ dnl Test the result and run user code.
+ AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1],
+ [m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])])
+])
+
+
+dnl =========================================================================
+dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl =========================================================================
+AC_DEFUN([AX_LUA_READLINE],
+[
+ AX_LIB_READLINE
+ AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' &&
+ test "x$ac_cv_header_readline_history_h" != 'x'],
+ [ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS"
+ $1
+ ],
+ [$2])
+])
diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 9bbb3660..920d258e 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -112,7 +112,7 @@ static int check_eof(void);
%option nounput
%option noreject
-%x COMMENT CCOMM CLI
+%x COMMENT CCOMM CLI MULTISTRING
ALPHA [a-zA-Z_]
DIGIT [0-9]
@@ -308,6 +308,18 @@ else: {
return TEXT;
}
+\"\"\" BEGIN(MULTISTRING);
+<MULTISTRING>\"\"\" {
+ yytext[yyleng-3] = 0;
+ cf_lval.t = cfg_strdup(yytext);
+ yytext[yyleng-3] = '\"';
+ BEGIN(INITIAL);
+ return TEXT;
+}
+<MULTISTRING><<EOF>> cf_error("Unterminated multi-line string");
+<MULTISTRING>\n ifs->lino++; ifs->chno = 0; yymore();
+<MULTISTRING>. yymore();
+
["][^"\n]*\n cf_error("Unterminated string");
<INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
diff --git a/configure.ac b/configure.ac
index da1a8f44..2b48c226 100644
--- a/configure.ac
+++ b/configure.ac
@@ -271,7 +271,7 @@ if test "$enable_mpls_kernel" != no ; then
fi
fi
-all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip $proto_rpki static"
+all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip $proto_rpki static wireguard"
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
@@ -289,6 +289,7 @@ AH_TEMPLATE([CONFIG_RADV], [RAdv protocol])
AH_TEMPLATE([CONFIG_RIP], [RIP protocol])
AH_TEMPLATE([CONFIG_RPKI], [RPKI protocol])
AH_TEMPLATE([CONFIG_STATIC], [Static protocol])
+AH_TEMPLATE([CONFIG_WIREGUARD], [Wireguard protocol])
AC_MSG_CHECKING([protocols])
protocols=`echo "$with_protocols" | sed 's/,/ /g'`
@@ -340,6 +341,12 @@ elif test "$bird_cv_lib_log" != yes ; then
LIBS="$LIBS $bird_cv_lib_log"
fi
+AX_PROG_LUA(5.3)
+AX_LUA_HEADERS
+AX_LUA_LIBS
+AC_SUBST(LUA_INCLUDE)
+AC_SUBST(LUA_LIBS)
+
if test "$enable_debug" = yes ; then
AC_DEFINE([DEBUGGING], [1], [Define to 1 if debugging is enabled])
LDFLAGS="$LDFLAGS -rdynamic"
diff --git a/filter/config.Y b/filter/config.Y
index c1e74531..6004c961 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -426,7 +426,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr
%type <fda> dynamic_attr
%type <fsa> static_attr
-%type <f> filter filter_body where_filter
+%type <f> filter filter_body where_filter lua_call
%type <i> type break_command ec_kind
%type <i32> cnum
%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
@@ -453,6 +453,7 @@ filter_def:
conf: filter_eval ;
filter_eval:
EVAL term { f_eval_int($2); }
+ | EVAL LUA constant { lua_eval($3); }
;
conf: custom_attr ;
@@ -543,6 +544,7 @@ declsn: one_decl { $$ = $1; }
filter_body:
function_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
+ f->type = FILTER_INTERNAL;
f->name = NULL;
f->root = $1;
$$ = f;
@@ -561,6 +563,7 @@ where_filter:
WHERE term {
/* Construct 'IF term THEN ACCEPT; REJECT;' */
struct filter *f = cfg_alloc(sizeof(struct filter));
+ f->type = FILTER_INTERNAL;
struct f_inst *i, *acc, *rej;
acc = f_new_inst(FI_PRINT_AND_DIE); /* ACCEPT */
acc->a1.p = NULL;
@@ -1064,6 +1067,11 @@ cmd:
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
;
+lua_call:
+ LUA constant {
+ $$ = lua_new_filter($2);
+ }
+
get_cf_position:
{
$$ = cf_text;
diff --git a/filter/filter.c b/filter/filter.c
index 37cf16a3..d047b814 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -1773,7 +1773,18 @@ f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int fla
LOG_BUFFER_INIT(f_buf);
- struct f_val res = interpret(filter->root);
+ struct f_val res;
+ switch (filter->type) {
+ case FILTER_INTERNAL:
+ res = interpret(filter->root);
+ break;
+ case FILTER_LUA:
+ ACCESS_EATTRS;
+ res = lua_interpret(filter->lua_chunk, rte, &f_old_rta, f_eattrs, tmp_pool, flags);
+ break;
+ default:
+ bug("filter type not set");
+ }
if (f_old_rta) {
/*
@@ -1867,5 +1878,16 @@ filter_same(struct filter *new, struct filter *old)
if (old == FILTER_ACCEPT || old == FILTER_REJECT ||
new == FILTER_ACCEPT || new == FILTER_REJECT)
return 0;
- return i_same(new->root, old->root);
+ if (new->type != old->type)
+ return 0;
+ switch(new->type) {
+ case FILTER_INTERNAL:
+ return i_same(new->root, old->root);
+ break;
+ case FILTER_LUA:
+ return lua_filter_same(new->lua_chunk, old->lua_chunk);
+ break;
+ default:
+ bug("Unknown filter type");
+ }
}
diff --git a/filter/filter.h b/filter/filter.h
index a8c33287..2b1176dc 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -145,8 +145,15 @@ struct f_static_attr {
};
struct filter {
+ enum filter_type {
+ FILTER_INTERNAL = 1,
+ FILTER_LUA = 2,
+ } type;
char *name;
- struct f_inst *root;
+ union {
+ struct f_inst *root;
+ struct lua_filter_chunk *lua_chunk;
+ };
};
struct f_inst *f_new_inst(enum f_instruction_code fi_code);
@@ -284,6 +291,8 @@ struct f_trie
};
#define NEW_F_VAL struct f_val * val; val = cfg_alloc(sizeof(struct f_val));
+#define F_VAL(_type, where, value) ((struct f_val) { .type = (_type), .val.where = (value) })
+#define F_VAL_VOID ((struct f_val) { .type = T_VOID })
#define FF_SILENT 2 /* Silent filter execution */
@@ -307,4 +316,10 @@ struct f_bt_test_suite {
/* Hook for call bt_assert() function in configuration */
extern void (*bt_assert_hook)(int result, struct f_inst *assert);
+/* Lua */
+struct filter * lua_new_filter(struct f_inst *inst);
+struct f_val lua_interpret(struct lua_filter_chunk *chunk, struct rte **e, struct rta **a, struct ea_list **ea, struct linpool *lp, int flags);
+int lua_filter_same(struct lua_filter_chunk *new, struct lua_filter_chunk *old);
+uint lua_eval(struct f_inst *inst);
+
#endif
diff --git a/lib/printf.c b/lib/printf.c
index c2065d9a..130bc61c 100644
--- a/lib/printf.c
+++ b/lib/printf.c
@@ -8,6 +8,7 @@
*/
#include "nest/bird.h"
+#include "conf/conf.h"
#include "string.h"
#include <errno.h>
diff --git a/lua/Makefile b/lua/Makefile
new file mode 100644
index 00000000..b74309de
--- /dev/null
+++ b/lua/Makefile
@@ -0,0 +1,4 @@
+src := common.c filter.c
+obj := $(src-o-files)
+$(all-daemon)
+#$(cf-local)
diff --git a/lua/common.c b/lua/common.c
new file mode 100644
index 00000000..9f617775
--- /dev/null
+++ b/lua/common.c
@@ -0,0 +1,323 @@
+#include "nest/bird.h"
+#include "nest/protocol.h"
+#include "nest/route.h"
+#include "conf/conf.h"
+#include "filter/filter.h"
+#include "lua.h"
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+static linpool *lua_lp;
+
+static int luaB_err(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 1)
+ log(L_WARN "bird.err() accepts exactly 1 argument");
+
+ if (n < 1)
+ return 0;
+
+ log(L_ERR "%s", lua_tostring(L, 1));
+ return 0;
+}
+
+static int luaB_warn(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 1)
+ log(L_WARN "bird.warn() accepts exactly 1 argument");
+
+ if (n < 1)
+ return 0;
+
+ log(L_WARN "%s", lua_tostring(L, 1));
+ return 0;
+}
+
+static int luaB_info(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 1)
+ log(L_WARN "bird.info() accepts exactly 1 argument");
+
+ if (n < 1)
+ return 0;
+
+ log(L_INFO "%s", lua_tostring(L, 1));
+ return 0;
+}
+
+static int luaB_trace(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 1)
+ log(L_WARN "bird.trace() accepts exactly 1 argument");
+
+ if (n < 1)
+ return 0;
+
+ log(L_TRACE "%s", lua_tostring(L, 1));
+ return 0;
+}
+
+#define lua_sett(L, idx, val, what) do { \
+ lua_pushstring(L, idx); \
+ lua_push##what(L, val); \
+ lua_settable(L, -3); \
+} while (0)
+
+#define lua_settableaddr(L, idx, val) lua_sett(L, idx, val, addr)
+#define lua_settablecfunction(L, idx, val) lua_sett(L, idx, val, cfunction)
+#define lua_settableinteger(L, idx, val) lua_sett(L, idx, val, integer)
+#define lua_settableip4(L, idx, val) lua_sett(L, idx, val, ip4)
+#define lua_settablelightuserdata(L, idx, val) lua_sett(L, idx, val, lightuserdata)
+#define lua_settableeattr(L, idx, val) lua_sett(L, idx, val, eattr)
+#define lua_settablevalue(L, idx, val) lua_sett(L, idx, val, value)
+
+#define lua_setglobalcfunction(L, n, val) do { \
+ lua_pushcfunction(L, val); \
+ lua_setglobal(L, n); \
+} while (0)
+
+static int luaB_generic_concat(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 2) {
+ log(L_WARN "__concat needs exactly 2 arguments");
+ return 0;
+ }
+
+ const char *a, *b;
+ size_t la, lb;
+
+ a = luaL_tolstring(L, 1, &la);
+ b = luaL_tolstring(L, 2, &lb);
+
+ if (a == NULL) {
+ a = "";
+ la = 0;
+ }
+
+ if (b == NULL) {
+ b = "";
+ lb = 0;
+ }
+
+ char *c = alloca(la + lb + 1);
+ memcpy(c, a, la);
+ memcpy(c + la, b, lb);
+ c[la + lb] = 0;
+
+ lua_pushlstring(L, c, la + lb);
+
+ return 1;
+}
+
+static int luaB_ip4_tostring(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 1) {
+ log(L_WARN "__tostring needs exactly 1 argument");
+ return 0;
+ }
+
+ lua_pushliteral(L, "addr");
+ lua_gettable(L, 1);
+ lua_Integer a = lua_tointeger(L, -1);
+ char c[IP4_MAX_TEXT_LENGTH];
+ bsnprintf(c, IP4_MAX_TEXT_LENGTH, "%I4", a);
+
+ lua_pushstring(L, c);
+ return 1;
+}
+
+static void lua_puship4(lua_State *L, ip4_addr a) {
+ lua_newtable(L);
+ lua_settableinteger(L, "addr", ip4_to_u32(a));
+
+ lua_newtable(L);
+ lua_settablecfunction(L, "__tostring", luaB_ip4_tostring);
+ lua_settablecfunction(L, "__concat", luaB_generic_concat);
+ lua_setmetatable(L, -2);
+}
+
+static int luaB_addr_tostring(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 1) {
+ log(L_WARN "__tostring needs exactly 1 argument");
+ return 0;
+ }
+
+ lua_pushliteral(L, "_internal");
+ lua_gettable(L, 1);
+ if (!lua_isuserdata(L, -1))
+ luaL_error(L, "fatal: bird internal state not found, type %d", lua_type(L, -1));
+
+ net_addr *addr = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+
+ char c[NET_MAX_TEXT_LENGTH+1];
+ net_format(addr, c, sizeof(c));
+ lua_pushstring(L, c);
+ return 1;
+}
+
+static void lua_pushaddr(lua_State *L, net_addr *addr) {
+ lua_newtable(L);
+ lua_settablelightuserdata(L, "_internal", addr);
+
+ lua_newtable(L);
+ lua_settablecfunction(L, "__tostring", luaB_addr_tostring);
+ lua_settablecfunction(L, "__concat", luaB_generic_concat);
+ lua_setmetatable(L, -2);
+}
+
+static void lua_pusheattr(lua_State *L, eattr *ea) {
+ /* if (ea->type == EAF_TYPE_IP_ADDRESS) { */
+ /* lua_settableinteger(L, "data", 17); */
+ /* /\* lua_pushaddr(L, "addr", (net_addr*)ea->u.ptr->data); *\/ */
+ /* } */
+ lua_newtable(L);
+ lua_settableinteger(L, "id", ea->id);
+ lua_settableinteger(L, "type", ea->type);
+ if (ea->u.ptr && ea->u.ptr->data) {
+ lua_pushliteral(L, "data");
+ lua_pushlstring(L, ea->u.ptr->data, ea->u.ptr->length);
+ lua_settable(L, -3);
+ }
+
+ /* lua_settablecfunction(L, "__tostring", luaB_addr_tostring); */
+ /* lua_settablecfunction(L, "__concat", luaB_generic_concat); */
+ /* lua_setmetatable(L, -2); */
+}
+
+static lua_bird_state *luaB_getinternalstate(lua_State *L) {
+ lua_getglobal(L, "bird");
+ lua_pushstring(L, "_internal_state");
+ lua_gettable(L, -2);
+ if (!lua_isuserdata(L, -1))
+ luaL_error(L, "fatal: bird internal state not found, type %d", lua_type(L, -1));
+
+ lua_bird_state *lbs = lua_touserdata(L, -1);
+ lua_pop(L, 2); /* Pop the user data and then the table. The string is consumed by gettable(). */
+ return lbs;
+}
+
+static int luaB_global_exception(lua_State *L, int value) {
+ int n = lua_gettop(L);
+ if (n > 1)
+ log(L_WARN "Called exception with too many arguments.");
+
+ lua_bird_state *lbs = luaB_getinternalstate(L);
+ lbs->exception = value;
+
+ lua_error(L);
+ return 0;
+}
+
+static inline int luaB_accept(lua_State *L) { return luaB_global_exception(L, F_ACCEPT); }
+static inline int luaB_reject(lua_State *L) { return luaB_global_exception(L, F_REJECT); }
+
+lua_bird_state *luaB_init(lua_State *L, struct linpool *lp) {
+ lua_newtable(L);
+
+ lua_settablecfunction(L, "err", luaB_err);
+ lua_settablecfunction(L, "warn", luaB_warn);
+ lua_settablecfunction(L, "info", luaB_info);
+ lua_settablecfunction(L, "trace", luaB_trace);
+
+ lua_bird_state *lbs = lp_allocz(lp, sizeof(lua_bird_state));
+
+ lua_settablelightuserdata(L, "_internal_state", lbs);
+
+ lua_settableip4(L, "router_id", ip4_from_u32(config->router_id));
+
+ lua_setglobal(L, "bird");
+
+ lua_pushcfunction(L, luaB_accept);
+ lua_setglobal(L, "accept");
+
+ lua_pushcfunction(L, luaB_reject);
+ lua_setglobal(L, "reject");
+
+ return lbs;
+}
+
+static int luaB_route_ea_find(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 2) {
+ log(L_WARN "ea_find needs exactly 1 argument");
+ return 0;
+ }
+
+ lua_pushliteral(L, "_internal");
+ lua_gettable(L, 1);
+ if (!lua_isuserdata(L, -1))
+ luaL_error(L, "fatal: bird internal state not found, type %d", lua_type(L, -1));
+
+ struct rte *e = lua_touserdata(L, -1);
+ int ea = lua_tointeger(L, 2);
+ lua_pop(L, 2);
+
+ struct ea_list *eattrs = e->attrs->eattrs;
+ eattr *t = ea_find(eattrs, ea);
+
+ if (t) {
+ lua_pusheattr(L, t);
+ return 1;
+ } else {
+ log(L_ERR "eattr not found");
+ return 0;
+ }
+}
+
+/* ea_set_attr_data(id, flags, type, data(string) */
+static int luaB_route_ea_set_attr_data(lua_State *L) {
+ int n = lua_gettop(L);
+ if (n != 5) {
+ log(L_WARN "ea_set_attr_data needs exactly 4 argument");
+ return 0;
+ }
+
+ lua_pushliteral(L, "_internal");
+ lua_gettable(L, 1);
+ if (!lua_isuserdata(L, -1))
+ luaL_error(L, "fatal: bird internal state not found, type %d", lua_type(L, -1));
+
+ struct rte *e = lua_touserdata(L, -1);
+ uint id = lua_tointeger(L, 2);
+ uint flags = lua_tointeger(L, 3);
+ uint type = lua_tointeger(L, 4);
+ size_t len = 0;
+ const char *data = lua_tolstring(L, 5, &len);
+ lua_pop(L, 5);
+
+ struct ea_list **eattrs = &e->attrs->eattrs;
+ if (!lua_lp)
+ lua_lp = lp_new_default(&root_pool);
+ ea_set_attr_data(eattrs, lua_lp, id, flags, type, data, len);
+ lua_pushboolean(L, 1);
+ return 0;
+}
+
+void luaB_push_route(lua_State *L, struct rte *e) {
+ lua_newtable(L);
+ lua_settablelightuserdata(L, "_internal", e);
+ lua_settableaddr(L, "prefix", e->net->n.addr);
+ lua_settablecfunction(L, "ea_find", luaB_route_ea_find);
+ lua_settablecfunction(L, "ea_set_attr_data", luaB_route_ea_set_attr_data);
+
+ lua_newtable(L);
+ lua_settablevalue(L, "__index", -2-1);
+ lua_setmetatable(L, -2);
+
+ lua_setglobal(L, "route");
+}
+
+void luaB_push_eattrs(lua_State *L, struct ea_list *ea) {
+ lua_newtable(L);
+
+ if (!ea) {
+ log(L_ERR "Null eattrs");
+ }
+
+ lua_settablecfunction(L, "__tostring", luaB_addr_tostring);
+ lua_setmetatable(L, -2);
+}
diff --git a/lua/filter.c b/lua/filter.c
new file mode 100644
index 00000000..f4de8f1f
--- /dev/null
+++ b/lua/filter.c
@@ -0,0 +1,187 @@
+#include "nest/bird.h"
+#include "conf/conf.h"
+#include "filter/filter.h"
+#include "lua.h"
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+/* Docs: http://pgl.yoyo.org/luai/i/luaL_dostring */
+
+static lua_State *global_lua_state = NULL;
+
+static inline lua_State * luaB_getstate(void) {
+ if (!global_lua_state) {
+ lua_State *L = luaL_newstate();
+ luaL_openlibs(L);
+ global_lua_state = L;
+ }
+
+ return lua_newthread(global_lua_state);
+}
+
+static inline void luaB_close(lua_State *L UNUSED) {
+ lua_pop(global_lua_state, 1);
+}
+
+struct lua_new_filter_writer_data {
+ struct lua_filter_chunk *first, *last;
+};
+
+static int lua_new_filter_writer(lua_State *L UNUSED, const void *p, size_t sz, void *ud) {
+ struct lua_new_filter_writer_data *d = ud;
+ struct lua_filter_chunk *cur = cfg_allocz(sizeof(struct lua_filter_chunk));
+
+ cur->size = sz;
+ cur->chunk = cfg_alloc(sz);
+ memcpy(cur->chunk, p, sz);
+
+ if (d->last)
+ d->last = d->last->next = cur;
+ else
+ d->last = d->first = cur;
+
+ return 0;
+}
+
+struct filter * lua_new_filter(struct f_inst *inst) {
+ struct filter *f = cfg_alloc(sizeof(struct filter));
+ f->name = NULL;
+ f->type = FILTER_LUA;
+
+ struct f_val string = f_eval(inst, cfg_mem);
+ if (string.type != T_STRING) {
+ cf_error("Lua filter must be a string");
+ return NULL;
+ }
+
+ lua_State *L = luaB_getstate();
+ int loadres = luaL_loadstring(L, string.val.s);
+ switch (loadres) {
+ case LUA_ERRMEM:
+ luaB_close(L);
+ cf_error("Memory allocation error occured when loading lua chunk");
+ return NULL;
+ case LUA_ERRSYNTAX:
+ {
+ const char *e = lua_tostring(L, -1);
+ char *ec = cfg_alloc(strlen(e) + 1);
+ strcpy(ec, e);
+ luaB_close(L);
+ cf_error("Lua syntax error: %s", ec);
+ return NULL;
+ }
+ case 0: /* Everything OK */
+ break;
+ }
+
+ struct lua_new_filter_writer_data lnfwd = {};
+ lua_dump(L, lua_new_filter_writer, &lnfwd, 0); /* No error to handle */
+ luaB_close(L);
+
+ f->lua_chunk = lnfwd.first;
+ return f;
+}
+
+static const char *lua_interpret_reader(lua_State *L UNUSED, void *ud, size_t *sz) {
+ struct lua_filter_chunk **cptr = ud;
+ if ((*cptr) == NULL)
+ return NULL;
+
+ *sz = (*cptr)->size;
+ void *out = (*cptr)->chunk;
+ *cptr = (*cptr)->next;
+ return out;
+}
+
+struct f_val lua_interpret(struct lua_filter_chunk *chunk, struct rte **e, struct rta **a UNUSED, struct ea_list **ea UNUSED, struct linpool *lp, int flags UNUSED) {
+ lua_State *L = luaB_getstate();
+
+ lua_bird_state *lbs = luaB_init(L, lp);
+ luaB_push_route(L, *e);
+ luaB_push_eattrs(L, *ea);
+
+ struct lua_filter_chunk **rptr = &chunk;
+ lua_load(L, lua_interpret_reader, rptr, "", "b");
+ int le = lua_pcall(L, 0, LUA_MULTRET, 0);
+ struct f_val out = F_VAL_VOID;
+ if (le && lbs->exception) {
+ out = F_VAL(T_RETURN, i, lbs->exception);
+ } else if (le) {
+ log(L_ERR "bad lua: %s", lua_tostring(L, -1));
+ out = F_VAL(T_RETURN, i, F_ERROR);
+ } else if (lua_isnumber(L, -1)) {
+ out = F_VAL(T_INT, i, lua_tonumber(L, -1));
+ } else {
+ log(L_WARN "lua return value is not a number (unimplemented): %s", lua_tostring(L, -1));
+ out = F_VAL(T_RETURN, i, F_ERROR);
+ }
+
+ *ea = (*e)->attrs->eattrs;
+
+ luaB_close(L);
+ return out;
+}
+
+int lua_filter_same(struct lua_filter_chunk *new, struct lua_filter_chunk *old) {
+ size_t npos = 0, opos = 0;
+ while (new && old) {
+ size_t nrem = new->size - npos;
+ size_t orem = old->size - opos;
+ size_t rem = MIN(nrem, orem);
+ if (memcmp(new->chunk + npos, old->chunk + opos, rem))
+ return 0;
+
+ npos += rem;
+ opos += rem;
+
+ if (npos == new->size) {
+ new = new->next;
+ npos = 0;
+ }
+
+ if (opos == old->size) {
+ old = old->next;
+ opos = 0;
+ }
+ }
+
+ if (!new && !old)
+ return 1;
+ else
+ return 0;
+}
+
+uint lua_eval(struct f_inst *inst)
+{
+ struct f_val string = f_eval(inst, cfg_mem);
+ if (string.type != T_STRING) {
+ cf_error("Lua filter must be a string");
+ return -1;
+ }
+
+ lua_State *L = luaB_getstate();
+ int dores = luaL_dostring(L, string.val.s);
+ log(L_WARN "lua_eval dores '%s' %d", string.val.s, dores);
+ switch (dores) {
+ case LUA_ERRMEM:
+ luaB_close(L);
+ cf_error("Memory allocation error occured when loading lua chunk");
+ return -1;
+ case LUA_ERRSYNTAX:
+ {
+ const char *e = lua_tostring(L, -1);
+ char *ec = cfg_alloc(strlen(e) + 1);
+ strcpy(ec, e);
+ luaB_close(L);
+ cf_error("Lua syntax error: %s", ec);
+ return -1;
+ }
+ case 0: /* Everything OK */
+ break;
+ }
+ luaB_close(L);
+
+ return 0;
+}
diff --git a/lua/lua.h b/lua/lua.h
new file mode 100644
index 00000000..fc9a52d2
--- /dev/null
+++ b/lua/lua.h
@@ -0,0 +1,18 @@
+#include "nest/bird.h"
+
+#include <lua.h>
+
+struct lua_filter_chunk {
+ size_t size;
+ void *chunk;
+ struct lua_filter_chunk *next;
+};
+
+typedef struct lua_bird_state {
+ int exception;
+} lua_bird_state;
+
+lua_bird_state *luaB_init(lua_State *L, struct linpool *lp);
+void luaB_push_route(lua_State *L, rte *e);
+void luaB_push_eattrs(lua_State *L, struct ea_list *ea);
+
diff --git a/nest/config.Y b/nest/config.Y
index aef5ed46..3242f96e 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -65,7 +65,7 @@ proto_postconfig(void)
CF_DECLS
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
-CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, TABLE, STATES, ROUTES, FILTERS)
+CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, LUA, NONE, VRF, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
@@ -263,6 +263,7 @@ rtable:
imexport:
FILTER filter { $$ = $2; }
+ | lua_call
| where_filter
| ALL { $$ = FILTER_ACCEPT; }
| NONE { $$ = FILTER_REJECT; }
diff --git a/nest/proto.c b/nest/proto.c
index d4a333d0..ccde5d9d 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -1382,6 +1382,9 @@ protos_build(void)
#ifdef CONFIG_PERF
proto_build(&proto_perf);
#endif
+#ifdef CONFIG_WIREGUARD
+ proto_build(&proto_wireguard);
+#endif
proto_pool = rp_new(&root_pool, "Protocols");
proto_shutdown_timer = tm_new(proto_pool);
diff --git a/nest/protocol.h b/nest/protocol.h
index 6c04071b..1fdfb380 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -53,6 +53,7 @@ enum protocol_class {
PROTOCOL_RIP,
PROTOCOL_RPKI,
PROTOCOL_STATIC,
+ PROTOCOL_WG,
PROTOCOL__MAX
};
@@ -102,7 +103,8 @@ void protos_dump_all(void);
extern struct protocol
proto_device, proto_radv, proto_rip, proto_static, proto_mrt,
proto_ospf, proto_perf,
- proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki;
+ proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki,
+ proto_wireguard;
/*
* Routing Protocol Instance
diff --git a/nest/route.h b/nest/route.h
index 8dfbb376..e37ef0ce 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -13,6 +13,10 @@
#include "lib/resource.h"
#include "lib/net.h"
+#ifdef CONFIG_WIREGUARD
+# include "sysdep/linux/wireguard.h"
+#endif /* CONFIG_WIREGUARD */
+
struct ea_list;
struct protocol;
struct proto;
@@ -203,6 +207,13 @@ struct hostentry {
byte dest; /* Chosen route destination type (RTD_...) */
byte nexthop_linkable; /* Nexthop list is completely non-device */
u32 igp_metric; /* Chosen route IGP metric */
+#ifdef CONFIG_WIREGUARD
+ union {
+ struct {
+ wg_key pub_key;
+ } wg;
+ } u;
+#endif
};
typedef struct rte {
@@ -605,7 +616,7 @@ ea_set_attr_ptr(ea_list **to, struct linpool *pool, uint id, uint flags, uint ty
{ ea_set_attr(to, pool, id, flags, type, (uintptr_t) val); }
static inline void
-ea_set_attr_data(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, void *data, uint len)
+ea_set_attr_data(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, const void *data, uint len)
{
struct adata *a = lp_alloc_adata(pool, len);
memcpy(a->data, data, len);
diff --git a/proto/wireguard/Makefile b/proto/wireguard/Makefile
new file mode 100644
index 00000000..26a7970f
--- /dev/null
+++ b/proto/wireguard/Makefile
@@ -0,0 +1,8 @@
+WG := $(srcdir)/../WireGuard/contrib/examples/embeddable-wg-library
+
+src := wireguard.c
+obj := $(src-o-files)
+$(all-daemon)
+$(cf-local)
+
+tests_objs := $(tests_objs) $(src-o-files)
diff --git a/proto/wireguard/config.Y b/proto/wireguard/config.Y
new file mode 100644
index 00000000..dfe621bd
--- /dev/null
+++ b/proto/wireguard/config.Y
@@ -0,0 +1,48 @@
+/*
+ * BIRD -- Wireguard Protocol Configuration
+ *
+ * (c) 1999 Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#include "proto/wireguard/wireguard.h"
+
+CF_DEFINES
+
+#define WG_CFG ((struct wg_config *) this_proto)
+
+CF_DECLS
+
+CF_KEYWORDS(WIREGUARD, PEERS)
+
+CF_GRAMMAR
+
+proto: wireguard_proto '}' ;
+
+wireguard_proto_start: proto_start WIREGUARD {
+ this_proto = proto_config_new(&proto_wireguard, $1);
+ }
+ ;
+
+wireguard_proto:
+ wireguard_proto_start proto_name '{'
+ | wireguard_proto proto_channel ';' { this_proto->net_type = $2->net_type; }
+ | wireguard_proto proto_item ';'
+ | wireguard_proto INTERFACE TEXT ';' { WG_CFG->ifname = $3; }
+ | wireguard_proto PEERS '{' peers '}'
+ ;
+
+peers:
+ /* empty */
+ | peers peer
+
+peer: ipa pubkey ';'
+
+pubkey: TEXT
+
+CF_CODE
+
+CF_END
diff --git a/proto/wireguard/wireguard.c b/proto/wireguard/wireguard.c
new file mode 100644
index 00000000..f45796d5
--- /dev/null
+++ b/proto/wireguard/wireguard.c
@@ -0,0 +1,222 @@
+// Based on proto/rip/rip.c
+
+#include <stdio.h>
+#include "nest/protocol.h"
+#include "nest/iface.h"
+#include "sysdep/linux/wireguard.h"
+#include "wireguard.h"
+#include "proto/bgp/bgp.h"
+
+static void
+wg_init_entry(void *e_)
+{
+ struct wg_entry *e = e_;
+// debug("wg_init_entry\n");
+}
+
+static void
+wg_postconfig(struct proto_config *C)
+{
+ C;
+ debug("postconfig\n");
+}
+
+static void
+wg_if_notify(struct proto *P, unsigned flags, struct iface *i)
+{
+ struct wg_proto *p = (struct wg_proto *) P;
+ struct wg_config *c = P->cf;
+
+ debug("WG: if_notify %p %s %d %s\n", i, i->name, flags, c->ifname);
+ if (c->ifname && !strcmp(i->name, c->ifname)) {
+ debug("WG: found ifname\n");
+ p->iface = i;
+ }
+}
+
+static void
+wg_rt_notify(struct proto *P, rtable *src_table, net *n,
+ rte *new, rte *old, ea_list *attrs)
+{
+ struct wg_proto *p = (struct wg_proto *) P;
+ struct wg_entry *en;
+ struct iface *iface = NULL;
+ const char *ifname = NULL;
+
+// debug("WG: notify\n");
+
+ if (new) {
+ iface = new->attrs->nh.iface;
+ ifname = iface ? iface->name : NULL;
+
+ if (iface && iface == p->iface) {
+ struct eattr *t;
+
+ debug("WG: found iface\n");
+ en = fib_get(&p->rtable, n->n.addr);
+
+ debug("WG: notify new %d %N\n",
+ new->attrs->dest, n->n.addr);
+
+ t = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_TUNNEL_ENCAP));
+ if (t) {
+ log(L_TRACE "WG: Attr %x %x", t->flags, t->type);
+/*
+ struct sub_tlv_remove_endpoint {
+ u32 asn;
+ u8 address_family;
+ union {
+ ip4_addr ip4;
+ ip6_addr ip6;
+ };
+ };
+
+ struct sub_tlv_udp_dest_port {
+ u16 port;
+ };
+
+ struct sub_tlv_wireguard {
+ u8 pubkey[32];
+ };
+
+ struct sub_tlv {
+ u8 type;
+ union {
+ struct {
+ u8 length;
+ struct sub_tlv_value value[];
+ } s8;
+ struct {
+ u16 length;
+ struct sub_tlv_value value[];
+ } s16;
+ } u;
+ };
+
+ struct bgp_tunnel_encap_attr {
+ u16 type;
+ u16 length;
+ struct sub_tlv stlv[];
+ };
+*/
+ } else {
+ log(L_TRACE "WG: No Attr");
+ }
+
+ // old_metric = en->valid ? en->metric : -1;
+
+// en->valid = RIP_ENTRY_VALID;
+// en->metric = rt_metric;
+// en->tag = rt_tag;
+// en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
+// en->iface = new->attrs->iface;
+// en->next_hop = new->attrs->gw;
+ }
+
+ } else {
+ debug("WG: notify withdraw\n");
+
+ /* Withdraw */
+ en = fib_find(&p->rtable, n->n.addr);
+
+ if (!en) // || en->valid != RIP_ENTRY_VALID)
+ return;
+
+/*
+ old_metric = en->metric;
+
+ en->valid = RIP_ENTRY_STALE;
+ en->metric = p->infinity;
+ en->tag = 0;
+ en->from = NULL;
+ en->iface = NULL;
+ en->next_hop = IPA_NONE;
+*/
+ }
+
+// TRACE(D_EVENTS, "wg notify %s %s", src_table->name, ifname?ifname:"(null)");
+}
+
+static int
+wg_reload_routes(struct proto *P)
+{
+ struct wg_proto *p = (struct wg_proto *) P;
+
+ debug("reload routes\n");
+
+// TODO
+// WALK_LIST(c, p->channels)
+// channel_request_feeding(c);
+
+ return 1;
+}
+
+
+static struct proto *
+wg_init(struct proto_config *C)
+{
+ struct wg_config *c = (struct pipe_config *) C;
+ struct proto *P = proto_new(C);
+ struct wg_proto *p = (struct wg_proto *) P;
+
+ debug("init\n");
+
+ P->main_channel = proto_add_channel(P, proto_cf_main_channel(C));
+
+ P->if_notify = wg_if_notify;
+ P->rt_notify = wg_rt_notify;
+ P->reload_routes = wg_reload_routes;
+// P->accept_ra_types = RA_ANY;
+
+ return P;
+}
+
+
+static int
+wg_start(struct proto *P)
+{
+ struct wg_config *cf = (struct wg_config *) P->cf;
+ struct wg_proto *p = (struct wg_proto *) P;
+
+ debug("start\n");
+ fib_init(&p->rtable, P->pool, P->net_type, sizeof(struct wg_entry),
+ OFFSETOF(struct wg_entry, n), 0, wg_init_entry);
+ return PS_UP;
+}
+
+static void
+wg_dump(struct proto *P)
+{
+ struct wg_proto *p = (struct wg_proto *) P;
+ int i;
+
+ i = 0;
+ FIB_WALK(&p->rtable, struct wg_entry, en)
+ {
+// struct wg_entry *en = (struct wg_entry *) e;
+ debug("WG: entry #%d:\n",
+ i++);
+ }
+ FIB_WALK_END;
+}
+
+
+struct protocol proto_wireguard = {
+ .name = "Wireguard",
+ .template = "wg%d",
+ .class = PROTOCOL_WG,
+ .channel_mask = NB_ANY,
+ .proto_size = sizeof(struct wg_proto),
+ .config_size = sizeof(struct wg_config),
+ .postconfig = wg_postconfig,
+ .init = wg_init,
+ .start = wg_start,
+ .dump = wg_dump,
+/* .multitable = 1,
+ .preference = DEF_PREF_PIPE,
+ .cleanup = wg_cleanup,
+ .reconfigure = wg_reconfigure,
+ .copy_config = wg_copy_config,
+ .get_status = wg_get_status,
+ .show_proto_info = wg_show_proto_info*/
+};
diff --git a/proto/wireguard/wireguard.h b/proto/wireguard/wireguard.h
new file mode 100644
index 00000000..992bb12b
--- /dev/null
+++ b/proto/wireguard/wireguard.h
@@ -0,0 +1,22 @@
+#ifndef _BIRD_WIREGUARD_H
+#define _BIRD_WIREGUARD_H
+
+#include "nest/protocol.h"
+
+struct wg_config {
+ struct proto_config c;
+ const char *ifname;
+};
+
+struct wg_proto {
+ struct proto p;
+ struct fib rtable;
+ struct iface *iface;
+};
+
+struct wg_entry {
+ struct fib_node n;
+};
+
+
+#endif /* _BIRD_WIREGUARD_H */