From 0810bba8b4b1ab23d3a0ca9101eaec45872e815d Mon Sep 17 00:00:00 2001 From: Jan Maria Matejka Date: Tue, 23 Jan 2018 15:11:08 +0100 Subject: [PATCH] lua filter saving and reusing --- filter/config.Y | 9 ++-- filter/filter.c | 33 ++++++++++----- filter/filter.h | 13 +++++- lua/filter.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++- lua/lua.h | 6 +++ 5 files changed, 148 insertions(+), 20 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index 6bba042d..8b5237b9 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -524,6 +524,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; @@ -542,6 +543,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(); /* ACCEPT */ acc->code = P('p',','); @@ -1060,12 +1062,7 @@ cmd: 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; + $$ = lua_new_filter($2); } get_cf_position: diff --git a/filter/filter.c b/filter/filter.c index 5a6a4447..0c7fba50 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -1575,14 +1575,6 @@ interpret(struct f_inst *what) CALL(bt_assert_hook, res.val.i, what); break; - case P('L','C'): /* Lua include */ - ONEARG; - if (v1.type != T_STRING) - runtime("Lua code should be a string argument"); - - res = filter_lua_chunk(v1.val.s, f_rte, f_old_rta, f_tmp_attrs, f_pool); - break; - default: bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff); } @@ -1778,7 +1770,17 @@ f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, struc 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: + res = lua_interpret(filter->lua_chunk, rte, &f_old_rta, tmp_attrs, tmp_pool, flags); + break; + default: + bug("filter type not set"); + } if (f_old_rta) { /* @@ -1883,5 +1885,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 ee77906b..b2a182a0 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -71,8 +71,15 @@ struct f_val { }; 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(void); @@ -223,6 +230,8 @@ struct f_bt_test_suite { extern void (*bt_assert_hook)(int result, struct f_inst *assert); /* Lua */ -struct f_val filter_lua_chunk(const char *chunk, struct rte **e, struct rta *a, struct ea_list **ea, struct linpool *lp); +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); #endif diff --git a/lua/filter.c b/lua/filter.c index 9d12fb16..fa005354 100644 --- a/lua/filter.c +++ b/lua/filter.c @@ -1,4 +1,5 @@ #include "nest/bird.h" +#include "conf/conf.h" #include "filter/filter.h" #include "lua.h" @@ -8,12 +9,85 @@ /* 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) { +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 = luaL_newstate(); + luaL_openlibs(L); + int loadres = luaL_loadstring(L, string.val.s); + switch (loadres) { + case LUA_ERRMEM: + lua_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); + lua_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 */ + lua_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, struct ea_list **ea, struct linpool *lp, int flags) { lua_State *L = luaL_newstate(); luaL_openlibs(L); lua_bird_state *lbs = luaB_init(L, lp); luaB_push_route(L, *e); - int le = luaL_dostring(L, chunk); + 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); @@ -30,3 +104,32 @@ struct f_val filter_lua_chunk(const char *chunk, struct rte **e, struct rta *a, lua_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; +} diff --git a/lua/lua.h b/lua/lua.h index 39582e2e..021f0ee8 100644 --- a/lua/lua.h +++ b/lua/lua.h @@ -2,6 +2,12 @@ #include +struct lua_filter_chunk { + size_t size; + void *chunk; + struct lua_filter_chunk *next; +}; + typedef struct lua_bird_state { int exception; } lua_bird_state;