]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
lua filter saving and reusing
authorJan Maria Matejka <mq@ucw.cz>
Tue, 23 Jan 2018 14:11:08 +0000 (15:11 +0100)
committerJan Maria Matejka <mq@ucw.cz>
Tue, 23 Jan 2018 14:17:01 +0000 (15:17 +0100)
filter/config.Y
filter/filter.c
filter/filter.h
lua/filter.c
lua/lua.h

index 6bba042d0a379da7484e3039085b29eb0c49c74b..8b5237b93b201a7a4f303e6063620117116b9746 100644 (file)
@@ -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:
index 5a6a4447fa64ccd52910f8bc152be261e63b629c..0c7fba502c1b9829508339e0838390fb96579dbe 100644 (file)
@@ -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");
+  }
 }
index ee77906b4c62c441e0f509deb3f8fc3903a28dd3..b2a182a0b8c09dd846c7b681c763c1eb8cbc3880 100644 (file)
@@ -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
index 9d12fb1660b3870733b43fce0d64a727c4bfdeb5..fa0053547ea0b9d74f6aa0db0b6749c3a4a3209f 100644 (file)
@@ -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;
+}
index 39582e2ee10d55c77e3316fdc372f8b95b332d8c..021f0ee84ae62062983ac51f5c38c1223e8cd12b 100644 (file)
--- a/lua/lua.h
+++ b/lua/lua.h
@@ -2,6 +2,12 @@
 
 #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;