#include "nest/bird.h" #include "nest/protocol.h" #include "nest/route.h" #include "nest/iface.h" #include "conf/conf.h" #include "filter/filter.h" #include "lua.h" #include "sysdep/linux/wireguard.h" #include #include #include static linpool *lua_lp; static void lua_pushwgdevice(lua_State *L, wg_device *dev); static void lua_pushwgpeer(lua_State *L, wg_peer *peer); 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; } static int luaB_wg_get_device(lua_State *L) { int n = lua_gettop(L); if (n != 1) { log(L_WARN "get_device needs exactly 1 argument"); return 0; } const char *device_name = lua_tolstring(L, 1, NULL); log(L_WARN "WG: device name %s", device_name); lua_pop(L, 1); wg_device *dev = NULL; if (wg_get_device(&dev, device_name) < 0) { return luaL_error(L, "Wireguard error opening %s", device_name); } log(L_WARN "WG: device %p", dev); lua_pushwgdevice(L, dev); return 1; } static int luaB_wg_device_first_peer(lua_State *L) { int n = lua_gettop(L); if (n != 1) { log(L_WARN "first_peer needs exactly 0 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)); wg_device *dev = lua_touserdata(L, -1); lua_pop(L, 1); lua_pushwgpeer(L, dev->first_peer); return 1; } static int luaB_wg_peer_next_peer(lua_State *L) { int n = lua_gettop(L); if (n != 1) { log(L_WARN "next_peer needs exactly 0 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)); wg_peer *peer = lua_touserdata(L, -1); lua_pop(L, 1); lua_pushwgpeer(L, peer->next_peer); return 1; } #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_settableipaddr(L, idx, val) lua_sett(L, idx, val, ipaddr) #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_settablewgdevice(L, idx, val) lua_sett(L, idx, val, wgdevice) #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 int luaB_ipaddr_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)); ip_addr *addr = lua_touserdata(L, -1); log(L_DEBUG "luaB_ipaddr_tostring %p", addr); lua_pop(L, 1); if (!addr) return 0; char c[IPA_MAX_TEXT_LENGTH+1]; if (ipa_is_ip4(*addr)) ip4_ntop(ipa_to_ip4(*addr), c); else ip6_ntop(ipa_to_ip6(*addr), c); lua_pushstring(L, c); return 1; } static void lua_pushipaddr(lua_State *L, ip_addr *addr) { lua_newtable(L); lua_settablelightuserdata(L, "_internal", addr); lua_newtable(L); lua_settablecfunction(L, "__tostring", luaB_ipaddr_tostring); lua_settablecfunction(L, "__concat", luaB_generic_concat); lua_setmetatable(L, -2); } static void lua_pushwgdevice(lua_State *L, wg_device *dev) { lua_newtable(L); lua_settablelightuserdata(L, "_internal", dev); lua_settablecfunction(L, "first_peer", luaB_wg_device_first_peer); lua_newtable(L); lua_settablevalue(L, "__index", -2-1); lua_setmetatable(L, -2); } static void lua_pushwgpeer(lua_State *L, wg_peer *peer) { lua_newtable(L); lua_settablelightuserdata(L, "_internal", peer); lua_settablecfunction(L, "next_peer", luaB_wg_peer_next_peer); wg_key_b64_string b64; wg_key_to_base64(b64, peer->public_key); lua_pushliteral(L, "public_key"); lua_pushlstring(L, b64, strlen(b64)); lua_settable(L, -3); lua_newtable(L); lua_settablevalue(L, "__index", -2-1); 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"); lua_newtable(L); lua_settablecfunction(L, "get_device", luaB_wg_get_device); lua_setglobal(L, "wireguard"); 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); log(L_INFO "rte flags %d", e->flags); if (e->attrs) { log(L_INFO "rta flags %d", e->attrs->aflags); if (e->attrs->eattrs) { log(L_INFO "eattr flags %d", e->attrs->eattrs->flags); } } log(L_WARN "******************************"); 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_settableipaddr(L, "gw", &e->attrs->nh.gw); const char *ifname = ""; if (e->attrs->nh.iface) { ifname = e->attrs->nh.iface->name; } lua_pushliteral(L, "ifname"); lua_pushlstring(L, ifname, strlen(ifname)); lua_settable(L, -3); 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); }