diff options
Diffstat (limited to 'lua')
-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 |
4 files changed, 532 insertions, 0 deletions
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); + |