diff --git a/filter/config.Y b/filter/config.Y index cd5a5b33..6bba042d 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -413,7 +413,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %nonassoc ELSE %type term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn dynamic_attr static_attr function_call symbol bgp_path_expr -%type filter filter_body where_filter +%type filter filter_body where_filter lua_call %type type break_command ec_kind %type cnum %type pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body @@ -1058,6 +1058,16 @@ cmd: | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); } ; +lua_call: + LUA constant { + $$ = cfg_alloc(sizeof(struct filter)); + $$->name = NULL; + $$->root = f_new_inst(); + $$->root->code = P('L','C'); + $$->root->a1.p = $2; + $$->root->next = NULL; + } + get_cf_position: { $$ = cf_text; diff --git a/filter/filter.c b/filter/filter.c index 62917b7b..5a6a4447 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1580,8 +1580,7 @@ interpret(struct f_inst *what) if (v1.type != T_STRING) runtime("Lua code should be a string argument"); - res.type = T_RETURN; - res.val.i = filter_lua_chunk(v1.val.s, f_rte, f_old_rta, f_tmp_attrs, f_pool); + res = filter_lua_chunk(v1.val.s, f_rte, f_old_rta, f_tmp_attrs, f_pool); break; default: diff --git a/filter/filter.h b/filter/filter.h index 4ee1776f..ee77906b 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -206,6 +206,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_FORCE_TMPATTR 1 /* Force all attributes to be temporary */ @@ -221,6 +223,6 @@ struct f_bt_test_suite { extern void (*bt_assert_hook)(int result, struct f_inst *assert); /* Lua */ -int filter_lua_chunk(const char *chunk, struct rte **e, struct rta *a, struct ea_list **ea, struct linpool *lp); +struct f_val filter_lua_chunk(const char *chunk, struct rte **e, struct rta *a, struct ea_list **ea, struct linpool *lp); #endif diff --git a/lua/common.c b/lua/common.c index d9467967..fad2f857 100644 --- a/lua/common.c +++ b/lua/common.c @@ -64,6 +64,12 @@ static int luaB_trace(lua_State *L) { #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_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); @@ -125,7 +131,34 @@ static void lua_puship4(lua_State *L, ip4_addr a) { lua_setmetatable(L, -2); } -void luaB_push_bird(lua_State *L) { +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); @@ -133,9 +166,21 @@ void luaB_push_bird(lua_State *L) { 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", 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; } void luaB_push_route(lua_State *L, struct rte *e) { diff --git a/lua/filter.c b/lua/filter.c index aae549ab..9d12fb16 100644 --- a/lua/filter.c +++ b/lua/filter.c @@ -6,21 +6,25 @@ #include #include -int filter_lua_chunk(const char *chunk, struct rte **e, struct rta *a, struct ea_list **ea, struct linpool *lp) { +/* Docs: http://pgl.yoyo.org/luai/i/luaL_dostring */ + +struct f_val filter_lua_chunk(const char *chunk, struct rte **e, struct rta *a, struct ea_list **ea, struct linpool *lp) { lua_State *L = luaL_newstate(); luaL_openlibs(L); - luaB_push_bird(L); + lua_bird_state *lbs = luaB_init(L, lp); luaB_push_route(L, *e); int le = luaL_dostring(L, chunk); - int out; - if (le) { - log(L_WARN "bad lua: %s", lua_tostring(L, -1)); - out = F_ERROR; + 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 = lua_tonumber(L, -1); + out = F_VAL(T_INT, i, lua_tonumber(L, -1)); } else { - log(L_WARN "lua return value is not a number: %s", lua_tostring(L, -1)); - out = F_ERROR; + log(L_WARN "lua return value is not a number (unimplemented): %s", lua_tostring(L, -1)); + out = F_VAL(T_RETURN, i, F_ERROR); } lua_close(L); diff --git a/lua/lua.h b/lua/lua.h index d2c522b9..39582e2e 100644 --- a/lua/lua.h +++ b/lua/lua.h @@ -2,5 +2,10 @@ #include -void luaB_push_bird(lua_State *L); +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); + diff --git a/nest/config.Y b/nest/config.Y index 6f8a49ae..58d50c6e 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -262,14 +262,7 @@ rtable: imexport: FILTER filter { $$ = $2; } - | LUA constant { - $$ = cfg_alloc(sizeof(struct filter)); - $$->name = NULL; - $$->root = f_new_inst(); - $$->root->code = P('L','C'); - $$->root->a1.p = $2; - $$->root->next = NULL; - } + | lua_call | where_filter | ALL { $$ = FILTER_ACCEPT; } | NONE { $$ = FILTER_REJECT; }