diff options
author | Sam Roberts <vieuxtech@gmail.com> | 2011-02-03 12:42:05 -0800 |
---|---|---|
committer | Sam Roberts <vieuxtech@gmail.com> | 2011-02-03 12:42:05 -0800 |
commit | 7d5d54e0422f74a9a1ad4ac879823a0739683595 (patch) | |
tree | e5e4fa37cf9c545fb2639fbeaf42dc2f526a0e1e /lua | |
parent | dce44a9ca28c5863e7661c3534f8983851177e6e (diff) | |
download | libnet-7d5d54e0422f74a9a1ad4ac879823a0739683595.tar.gz |
nfct and nfq support incremental setup and callbacks
Diffstat (limited to 'lua')
-rw-r--r-- | lua/nfct.c | 50 | ||||
-rw-r--r-- | lua/nflua.h | 46 | ||||
-rw-r--r-- | lua/nfq.c | 300 |
3 files changed, 349 insertions, 47 deletions
@@ -34,6 +34,7 @@ I make full userdata out of one or both of them, thats what it has to be. Don't confuse them, or you will segfault! */ + #include "lua.h" #include "lauxlib.h" #include "lualib.h" @@ -55,15 +56,10 @@ confuse them, or you will segfault! #include <libnetfilter_conntrack/libnetfilter_conntrack.h> -#define NFCT_REGID "wt.nfct" +#include "nflua.h" +#define NFCT_REGID "wt.nfct" -static void push_error(lua_State* L) -{ - lua_pushnil(L); - lua_pushstring(L, strerror(errno)); - lua_pushinteger(L, errno); -} static struct nf_conntrack* check_ct(lua_State*L) { @@ -96,7 +92,7 @@ static const char* ctmsg_type_string(enum nf_conntrack_msg_type type) /*- -- cthandle = nfct.open(subsys, [subscription...]) +-- cthandle = nfct.open(subsys, [subscription...]) subsys is "track" or "expect" @@ -166,7 +162,7 @@ static int open(lua_State *L) } /*- -- nfct.close(cthandle) +-- nfct.close(cthandle) Close the conntrack handle, freeing its resources. */ @@ -178,7 +174,7 @@ static int gc(lua_State* L) } /*- -- fd = nfct.fd(cthandle) +-- fd = nfct.fd(cthandle) Return the underlying fd used by the conntrack handle, useful for selecting on. @@ -231,7 +227,7 @@ static int cb( /*- -- cthandle = nfct.callback_register(cthandle, ctmsgtype) +-- cthandle = nfct.callback_register(cthandle, ctmsgtype) ctmsgtype is one of "new", "update", "destroy", or "all" (default is "all"). @@ -269,9 +265,10 @@ static int callback_register(lua_State* L) } /*- -- cthandle = nfct.catch(cthandle, cbfn) +-- cthandle = nfct.catch(cthandle, cbfn) +-- verdict = cbfn(ctmsgtype, ct) -cbfn is the callback function, and will be called as +cbfn - the callback function, and will be called as function cbfn(ctmsgtype, ct) ... @@ -324,7 +321,7 @@ static int catch(lua_State* L) } /*- -- nfct.loop(cthandle, ctmsgtype, cbfn) +-- nfct.loop(cthandle, ctmsgtype, cbfn) Equivalent to @@ -332,8 +329,7 @@ Equivalent to return nfct.catch(cthandle, cbfn) Registering callbacks repeatedly is unnecessarily slow, so this is best used on -blocking netlink sockets for scripts that do nothing but use the conntrack -subsystem. +blocking netlink sockets by scripts that use only the conntrack subsystem. */ static int loop(lua_State* L) { @@ -350,7 +346,7 @@ static int loop(lua_State* L) } /*- -- ct = nfct.new() +-- ct = nfct.new() Create a new conntrack context (NOT a conntrack handle). @@ -373,7 +369,7 @@ static int new(lua_State* L) } /*- -- nfct.destroy(ct) +-- nfct.destroy(ct) Destroy a conntrack context. @@ -389,7 +385,7 @@ static int destroy(lua_State* L) } /*- -- ct = nfct.setobjopt(ct, option) +-- ct = nfct.setobjopt(ct, option) Sets an option on a conntrack context, option is one of: "undo-snat", @@ -561,9 +557,9 @@ static enum nf_conntrack_attr check_attr(lua_State* L) } /*- -- value = nfct.get_attr_u8(ct, attr) -- value = nfct.get_attr_u16(ct, attr) -- value = nfct.get_attr_u32(ct, attr) +-- value = nfct.get_attr_u8(ct, attr) +-- value = nfct.get_attr_u16(ct, attr) +-- value = nfct.get_attr_u32(ct, attr) No error checking is done, values of zero will be returned for attributes that aren't present, and undefined values will be returned @@ -638,9 +634,9 @@ See enum nf_conntrack_attr (the aliases are not supported) /* TODO this could have a much better API, but I've no time for this now. */ /*- -- ct = nfct.set_attr_u8(ct, attr, value) -- ct = nfct.set_attr_u16(ct, attr, value) -- ct = nfct.set_attr_u32(ct, attr, value) +-- ct = nfct.set_attr_u8(ct, attr, value) +-- ct = nfct.set_attr_u16(ct, attr, value) +-- ct = nfct.set_attr_u32(ct, attr, value) No error checking is done, value will be cast to the necessary type, and who knows what will happen for values that aren't actually of the correct type for @@ -672,8 +668,8 @@ ATTR_UX(u16) ATTR_UX(u32) /*- -- h = nfct.ntohs(n) -- n = nfct.htons(h) +-- h = nfct.ntohs(n) +-- n = nfct.htons(h) Convert a short between network and host byte order. No error or bounds checking on the numbers is done. diff --git a/lua/nflua.h b/lua/nflua.h new file mode 100644 index 0000000..a831bf5 --- /dev/null +++ b/lua/nflua.h @@ -0,0 +1,46 @@ +/* +Copyright (C) 2011 Wurldtech Security Technologies All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Code common to the lua netfilter bindings. */ + +static const char* nfemsg(int eno) +{ + switch(errno) { + case EAGAIN: return "timeout"; + } + return strerror(eno); +} +static int push_error(lua_State* L) +{ + lua_pushnil(L); + lua_pushstring(L, nfemsg(errno)); + lua_pushinteger(L, errno); + + return 3; +} + + @@ -1,5 +1,5 @@ /* -Copyright (C) 2010 Wurldtech Security Technologies All rights reserved. +Copyright (C) 2011 Wurldtech Security Technologies All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -25,7 +25,9 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* +/*- +** nfq - a binding to netfilter's queue subsystem + list rules: @@ -42,7 +44,6 @@ sudo iptables -t filter -I INPUT 1 -p udp -j QUEUE replace input rule 1: sudo iptables -t filter -R INPUT 1 -p udp -j QUEUE - */ @@ -66,8 +67,37 @@ sudo iptables -t filter -R INPUT 1 -p udp -j QUEUE #include <libnetfilter_queue/libnetfilter_queue.h> +#include "nflua.h" + #define NFQ_REGID "wt.nfq" +static struct nfq_handle *check_handle(lua_State*L) +{ + struct nfq_handle* h = lua_touserdata(L, 1); + + luaL_argcheck(L, h, 1, "handle not provided"); + + return h; +} + +static struct nfq_q_handle *check_queue(lua_State*L) +{ + struct nfq_q_handle* q = lua_touserdata(L, 1); + + luaL_argcheck(L, q, 1, "queue not provided"); + + return q; +} + +static struct nfq_data *check_qdata(lua_State*L) +{ + struct nfq_data *qdata = lua_touserdata(L, 1); + + luaL_argcheck(L, qdata, 1, "qdata not provided"); + + return qdata; +} + static int cb( struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, @@ -75,6 +105,8 @@ static int cb( void *data ) { + /* TODO - should have an option "delay", to explicitly avoid + offering a verdict right away */ static const char* verdict_opt[] = { "accept", "drop", NULL }; @@ -113,16 +145,167 @@ static int cb( } } + + +/*- +-- handle = nfq.open() + +Return an nfqueue handle on success, or nil,emsg,errno on failure. +*/ +static int open(lua_State* L) +{ + struct nfq_handle *h = nfq_open(); + + if(!h) { + return push_error(L); + } + + lua_pushlightuserdata(L, h); + + return 1; +} + +/*- +-- nfq.close(handle) + +Close the handle, freeing its resources. +*/ +static int gc(lua_State* L) +{ + struct nfq_handle* h = check_handle(L); + nfq_close(h); + return 0; +} + +/*- +- fd = nfq.fd(handle) + +Return the underlying fd used by the handle, useful for +selecting on. +*/ +static int fd(lua_State* L) +{ + struct nfq_handle* h = check_handle(L); + lua_pushinteger(L, nfq_fd(h)); + return 1; +} + +static int check_pf(lua_State* L, int narg) +{ + /* TODO ... other values from /usr/include/bits/socket.h */ + static const char* pf_opts[] = { + "inet", + "inet6", + NULL + }; + static int pf_vals[] = { + PF_INET, + PF_INET6, + }; + int pf_opt = luaL_checkoption(L, narg, NULL, pf_opts); + int pf_val = pf_vals[pf_opt]; + + return pf_val; +} + +/*- +-- handle = nfq.unbind_pf(handle, family) + +Protocol family is one of "inet", "inet6". + +Return is handle on success and nil,emsg,errno on failure. +*/ +static int unbind_pf(lua_State* L) +{ + struct nfq_handle* h = check_handle(L); + int pf = check_pf(L, 2); + + if (nfq_unbind_pf(h, pf) < 0) { + return push_error(L); + } + return 1; +} + +/*- +-- handle = nfq.bind_pf(handle, family) + +Protocol family is one of "inet", "inet6". + +Note that sample code seems to always unbind before binding, I've no idea why, +and there is no indication of whether its possible to bind to multiple address +families. + +Return is handle on success and nil,emsg,errno on failure. +*/ +static int bind_pf(lua_State* L) +{ + struct nfq_handle* h = check_handle(L); + int pf = check_pf(L, 2); + + if (nfq_bind_pf(h, pf) < 0) { + return push_error(L); + } + return 1; +} + /*- -- nfq.loop(cb, copy) +-- handle = nfq.catch(handle, cbfn) +-- verdict = cbfn(qdata) -cb - a function called for every queued packet, it returns -"accept" or "drop" meaning to do that to the packet. For -no return value, the default is "accept". If it returns a second -argument, it must be a string, and replaces the current -packet. +cbfn - a function called for every queued packet with one argument, qdata. It +returns "accept" or "drop" meaning to do that to the packet. For no return +value, the default is "accept". If it returns a second argument, it must be a +string, and replaces the current packet. + +Return handle on success and nil,emsg,errno on failure. +*/ +/* TODO we allow only one cbfn for all the queues, which differs from + the underlying library which allows a cbfn per queue. To do that I'd have to + build a table to map the queues to their lua cbfns, which is possible, but I + don't have the time for right now. + */ +static int catch(lua_State *L) +{ + struct nfq_handle* h = check_handle(L); + int nffd = nfq_fd(h); + char buf[4096] __attribute__ ((aligned)); + ssize_t bufsz; + + while ((bufsz = recv(nffd, buf, sizeof(buf), 0)) > 0) { + if(nfq_handle_packet(h, buf, bufsz) < 0) { + return push_error(L); + } + } + + /* If we get here bufsz is <= 0, so either the netlink socket + closed (possible?), would block, or some other error occurred. */ + if(bufsz == 0) { + lua_pushnil(L); + lua_pushstring(L, "closed"); + return 2; + } + + return push_error(L); +} + +/*- +-- loop = nfq.loop(cb, copy) + +A one shot way to catch on queue zero, the equivalent of: + + h = nfq.open() + nfq.unbind_pf(h, "inet") + nfq.bind_pf(h, "inet") + q = nfq.create_queue(h, 0) + nfq.set_mode(q, copy, 0xffff) + ... = nfq.catch(h, cb) + nfq.destroy_queue(q) + nfq.close(h) + return ... + +DEPRECATED - don't use it in new code, it will be deleted as soon as +the existing users of it have been updated. -copy - "none", "meta", "packet", default to "packet" */ static int loop(lua_State *L) { @@ -136,7 +319,7 @@ static int loop(lua_State *L) int af = AF_INET; /* Could be an argument, if we ever did non-INET. */ struct nfq_handle *h = NULL; struct nfq_q_handle *qh = NULL; - int fd = -1; + int nlfd = -1; int nreturn = 0; char buf[4096] __attribute__ ((aligned)); ssize_t recvsz; @@ -160,9 +343,9 @@ static int loop(lua_State *L) if (nfq_set_mode(qh, copy, 0xffff /* larger than an ethernet frame */) < 0) goto err; - fd = nfq_fd(h); + nlfd = nfq_fd(h); - while ((recvsz = recv(fd, buf, sizeof(buf), 0)) >= 0) { + while ((recvsz = recv(nlfd, buf, sizeof(buf), 0)) >= 0) { nfq_handle_packet(h, buf, recvsz); } @@ -185,25 +368,87 @@ cleanup: nfq_close(h); return nreturn; + +} + + +/*- +-- queue = nfq.create_queue(handle, queuenum) + +queuenum is number of the queue to bind to. + +Return a queue on success, or nil,emsg,errno on failure. +*/ +static int create_queue(lua_State* L) +{ + struct nfq_handle* h = check_handle(L); + int num = luaL_checkint(L, 2); + struct nfq_q_handle *q = nfq_create_queue(h, num, cb, L); + + if(!q) { + return push_error(L); + } + + lua_pushlightuserdata(L, q); + + return 1; } -struct nfq_data *checkudata(lua_State*L) +/*- +-- nfq.destroy_queue(queue) + +Close the queue, freeing its resources. +*/ +static int destroy_queue(lua_State* L) { - struct nfq_data *nfqdata = lua_touserdata(L, 1); + struct nfq_q_handle* q = check_queue(L); + nfq_destroy_queue(q); + return 0; +} + +/*- +-- queue = nfq.set_mode(queue, copy, range) + +queue is a queue handle returned by nfq.create_queue(). - luaL_argcheck(L, nfqdata, 1, "nfqdata not provided"); +copy is one of "none" (a no-op, don't use it), "meta" (copy just packet +metadata), or "packet" (copy the full packet) (default is currently "packet"). - return nfqdata; +range is the size of the packet to copy, and is optional (it defaults to +0xffff, larger than any ethernet packet can be, and larger than any link +layer packet I'm aware of). + +Returns the queue on success and nil,emsg,errno on failure. +*/ +static int set_mode(lua_State* L) +{ + static const char* copy_opts[] = { + "none", "meta", "packet", NULL + }; + static int copy_vals[] = { + NFQNL_COPY_NONE, NFQNL_COPY_META, NFQNL_COPY_PACKET + }; + struct nfq_q_handle *q = check_queue(L); + int copy_opt = luaL_checkoption(L, 2, "packet", copy_opts); + int copy_val = copy_vals[copy_opt]; + int range = luaL_optint(L, 3, 0xffff); + + if (nfq_set_mode(q, copy_val, range) < 0) { + return push_error(L); + } + + return 1; } +/* FIXME - need to reimplement loop... for backwards compatibility! */ /*- -str = nfq.get_payload(cbctx) +-- str = nfq.get_payload(cbctx) -str is the IP payload, it has been stripped of link-layer headers! +str is the IP payload, it has been stripped of link-layer headers. */ static int get_payload(lua_State* L) { - struct nfq_data *nfqdata = checkudata(L); + struct nfq_data *nfqdata = check_qdata(L); char* data = NULL; int datasz = nfq_get_payload(nfqdata, &data); luaL_argcheck(L, datasz >= 0, 1, "nfqdata not available"); @@ -215,7 +460,22 @@ static int get_payload(lua_State* L) static const luaL_reg nfq[] = { + /* return or operate on handle */ + {"open", open}, + {"close", gc}, + {"fd", fd}, + {"unbind_pf", unbind_pf}, + {"bind_pf", bind_pf}, + {"catch", catch}, {"loop", loop}, + + /* return or operate on a queue */ + {"create_queue", create_queue}, + {"destroy_queue", destroy_queue}, + {"set_mode", set_mode}, + + + {"get_payload", get_payload}, {NULL, NULL} }; |