summaryrefslogtreecommitdiff
path: root/lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua')
-rw-r--r--lua/Makefile4
-rw-r--r--lua/common.c323
-rw-r--r--lua/filter.c187
-rw-r--r--lua/lua.h18
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);
+