Last active
March 4, 2021 11:46
-
-
Save vavrusa/926ced5fb2944da4fa66 to your computer and use it in GitHub Desktop.
Lua/C DHCP example#2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- C definitions | |
local ffi = require('ffi') | |
local csym = ffi.C | |
ffi.cdef[[ | |
/* DHCP header format */ | |
struct __attribute__((packed)) dhcp_msg { | |
/* Header */ | |
uint8_t op; | |
uint8_t htype; | |
uint8_t hlen; | |
uint8_t hops; | |
uint32_t xid; | |
uint16_t secs; | |
uint16_t flags; | |
/* Address fields */ | |
char _ciaddr[4], _yiaddr[4], _siaddr[4], _giaddr[4]; | |
char _chaddr[16]; | |
}; | |
/* Constants */ | |
struct dhcp_op { | |
static const int DISCOVER = 0x01; | |
static const int OFFER = 0x02; | |
}; | |
/* C functions */ | |
void yiaddr_set(char *msg, const char *yiaddr); | |
]] | |
-- Message meta type | |
local char_ptr = ffi.typeof('char *') | |
local dhcp_op = ffi.new('struct dhcp_op') | |
local dhcp_msg_t = ffi.typeof('struct dhcp_msg') | |
ffi.metatype( dhcp_msg_t, { | |
__index = { | |
chaddr = function(msg) | |
return ffi.string(msg._chaddr, 16) | |
end, | |
yiaddr = function(msg, addr) | |
if addr then | |
csym.yiaddr_set(msg, addr) | |
else | |
return ffi.string(msg._yiaddr, 4) | |
end | |
end, | |
}, | |
}) | |
-- Export public API | |
local dhcp = { | |
-- This is a way to export constants | |
op = dhcp_op, | |
-- Export types | |
msg_t = function (udata) return ffi.cast('struct dhcp_msg *', udata) end, | |
-- API functions | |
is_request = function (msg) return msg.op == dhcp_op.DISCOVER end, | |
} | |
return dhcp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <lua.h> | |
#include <lualib.h> | |
#include <lauxlib.h> | |
#include <stdint.h> | |
#include <arpa/inet.h> | |
/* @note Trivial example of a C callback */ | |
void yiaddr_set(char *msg, const char *yiaddr) | |
{ | |
inet_pton(AF_INET, yiaddr, msg + 16); | |
} | |
static int on_recv(lua_State *L, int cb_ref, char *buf, size_t len) | |
{ | |
lua_rawgeti(L, LUA_REGISTRYINDEX, cb_ref); | |
lua_pushlightuserdata(L, buf); | |
lua_pushinteger(L, len); | |
return lua_pcall(L, 2, 1, 0); | |
} | |
/* Convenience stuff */ | |
static void close_state(lua_State **L) { lua_close(*L); } | |
#define cleanup(x) __attribute__((cleanup(x))) | |
#define auto_lclose cleanup(close_state) | |
int main(int argc, char *argv[]) | |
{ | |
/* Create VM state */ | |
auto_lclose lua_State *L = luaL_newstate(); | |
if (!L) | |
return 1; | |
luaL_openlibs(L); | |
/* Load config file */ | |
if (argc > 1) { | |
luaL_loadfile(L, argv[1]); /* (1) */ | |
int ret = lua_pcall(L, 0, 0, 0); | |
if (ret != 0) { | |
fprintf(stderr, "%s\n", lua_tostring(L, -1)); | |
return 1; | |
} | |
} | |
/* Read out config */ | |
lua_getglobal(L, "address"); /* (2) */ | |
lua_getglobal(L, "port"); | |
printf("address: %s, port: %ld\n", /* (3) */ | |
lua_tostring(L, -2), lua_tointeger(L, -1)); | |
lua_settop(L, 0); /* (4) */ | |
/* Register bindings */ | |
lua_getglobal(L, "callback"); | |
int cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); | |
/* Prepare mock datagram. */ | |
char dhcp_req[512] = { | |
0x01, 0x01, 0x06, 0x00 | |
}; | |
char dgram[sizeof(dhcp_req)]; | |
/* Call a few times, no I/O yet. */ | |
const size_t nr_calls = 1000000; | |
for (unsigned i = 0; i < nr_calls; ++i) { | |
dgram[0] = 0x01; /* reset to DHCPDISCOVER */ | |
int ret = on_recv(L, cb_ref, dgram, sizeof(dgram)); | |
if (ret != 0) { | |
printf("%s\n", lua_tostring(L, -1)); | |
return 1; | |
} | |
lua_pop(L, 1); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
address = '255.255.255.0' | |
port = 67 | |
-- Require dhcp bindings | |
local dhcp = require('dhcp') | |
-- Only allow hwaddr with these prefixes | |
local allowed_prefix = { '\0\0', '\5\3' } | |
local function acl(hwaddr) | |
for i,v in ipairs(allowed_prefix) do | |
if hwaddr:sub(0, #v) == v then | |
return true | |
end | |
end | |
return false | |
end | |
-- Flip some bits in the buffer and return length | |
local lease = {} | |
function callback(buf, len) | |
local msg = dhcp.msg_t(buf) | |
if dhcp.is_request(msg) then -- DHCPDISCOVER | |
msg.op = dhcp.op.OFFER -- DHCPOFFER | |
local hwaddr = msg:chaddr() | |
if not acl(hwaddr) then return 0 end | |
local client_ip = lease[hwaddr] | |
if client_ip then -- keep leased addr | |
msg:yiaddr(client_ip) | |
else -- stub address range (: | |
client_ip = '192.168.1.1' | |
msg:yiaddr(client_ip) | |
lease[hwaddr] = client_ip | |
end | |
end | |
return len | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment