diff options
-rw-r--r-- | Makefile.in | 6 | ||||
-rw-r--r-- | aclocal.m4 | 480 | ||||
-rw-r--r-- | conf/cf-lex.l | 14 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | filter/config.Y | 10 | ||||
-rw-r--r-- | filter/filter.c | 26 | ||||
-rw-r--r-- | filter/filter.h | 17 | ||||
-rw-r--r-- | lib/printf.c | 1 | ||||
-rw-r--r-- | lua/Makefile | 4 | ||||
-rw-r--r-- | lua/common.c | 323 | ||||
-rw-r--r-- | lua/filter.c | 187 | ||||
-rw-r--r-- | lua/lua.h | 18 | ||||
-rw-r--r-- | nest/config.Y | 3 | ||||
-rw-r--r-- | nest/proto.c | 3 | ||||
-rw-r--r-- | nest/protocol.h | 4 | ||||
-rw-r--r-- | nest/route.h | 13 | ||||
-rw-r--r-- | proto/wireguard/Makefile | 8 | ||||
-rw-r--r-- | proto/wireguard/config.Y | 48 | ||||
-rw-r--r-- | proto/wireguard/wireguard.c | 222 | ||||
-rw-r--r-- | proto/wireguard/wireguard.h | 22 |
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 @@ -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 */ |