Skip to content

Instantly share code, notes, and snippets.

@vavrusa
Last active March 4, 2021 11:46
Show Gist options
  • Save vavrusa/926ced5fb2944da4fa66 to your computer and use it in GitHub Desktop.
Save vavrusa/926ced5fb2944da4fa66 to your computer and use it in GitHub Desktop.
Lua/C DHCP example#2
-- 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
#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;
}
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