1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00

Merge from /nmap-exp/patrick/nse-nsock-maintenance.

This is a maintenance fix for the NSE Nsock library binding. The patch focuses
on code correctness and simplicity. The patch also brings some initial updates
with an eye towards the upcoming Lua 5.2 release. See [1] for a post concerning
this branch.

[1] http://seclists.org/nmap-dev/2010/q3/710
This commit is contained in:
batrick
2010-09-18 20:35:09 +00:00
parent 5f13514d46
commit de4ba536de
20 changed files with 1446 additions and 2167 deletions

View File

@@ -82,9 +82,9 @@ UNINSTALLZENMAP=@UNINSTALLZENMAP@
UNINSTALLNPING=@UNINSTALLNPING@
ifneq (@LIBLUA_LIBS@,)
NSE_SRC=nse_main.cc nse_nsock.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc nse_binlib.cc nse_bit.cc
NSE_HDRS=nse_main.h nse_nsock.h nse_fs.h nse_nmaplib.h nse_debug.h nse_pcrelib.h nse_binlib.h nse_bit.h
NSE_OBJS=nse_main.o nse_nsock.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o nse_binlib.o nse_bit.o
NSE_SRC=nse_main.cc nse_utility.cc nse_nsock.cc nse_dnet.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc nse_binlib.cc nse_bit.cc
NSE_HDRS=nse_main.h nse_utility.h nse_nsock.h nse_dnet.h nse_fs.h nse_nmaplib.h nse_debug.h nse_pcrelib.h nse_binlib.h nse_bit.h
NSE_OBJS=nse_main.o nse_utility.o nse_nsock.o nse_dnet.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o nse_binlib.o nse_bit.o
ifneq (@OPENSSL_LIBS@,)
NSE_SRC+=nse_openssl.cc nse_ssl_cert.cc
NSE_HDRS+=nse_openssl.h nse_ssl_cert.h

View File

@@ -278,6 +278,10 @@
RelativePath="..\nse_main.cc"
>
</File>
<File
RelativePath="..\nse_utility.cc"
>
</File>
<File
RelativePath="..\nse_nmaplib.cc"
>
@@ -286,6 +290,10 @@
RelativePath="..\nse_nsock.cc"
>
</File>
<File
RelativePath="..\nse_dnet.cc"
>
</File>
<File
RelativePath="..\nse_openssl.cc"
>
@@ -479,6 +487,10 @@
RelativePath="..\nse_main.h"
>
</File>
<File
RelativePath="..\nse_utility.h"
>
</File>
<File
RelativePath="..\nse_nmaplib.h"
>
@@ -487,6 +499,10 @@
RelativePath="..\nse_nsock.h"
>
</File>
<File
RelativePath="..\nse_dnet.h"
>
</File>
<File
RelativePath="..\nse_openssl.h"
>

313
nse_dnet.cc Normal file
View File

@@ -0,0 +1,313 @@
#include "nsock.h"
#include "nmap_error.h"
#include "NmapOps.h"
#include "utils.h"
#include "tcpip.h"
#include "protocols.h"
#include "libnetutil/netutil.h"
#include "nse_main.h"
#include "nse_utility.h"
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
#include <assert.h>
extern NmapOps o;
/* Map of dnet userdata to ethernet device userdata */
#define ETH_CACHE_DNET_ETH 0
/* Map of ethernet device string identifier to ethernet device userdata */
#define ETH_CACHE_DEVICE_ETH 1
/* metatable entries in the registry */
#define DNET_METATABLE "DNET_METATABLE"
#define DNET_ETH_METATABLE "DNET_ETH_METATABLE"
typedef struct nse_dnet_udata
{
eth_t *eth;
int sock; /* raw ip socket */
int interface; /* integer reference in registry for interface string */
} nse_dnet_udata;
LUALIB_API int l_dnet_new (lua_State *L)
{
nse_dnet_udata *udata;
udata = (nse_dnet_udata *) lua_newuserdata(L, sizeof(nse_dnet_udata));
luaL_getmetatable(L, DNET_METATABLE);
lua_setmetatable(L, -2);
udata->eth = NULL;
udata->sock = -1;
udata->interface = LUA_NOREF;
return 1;
}
LUALIB_API int l_dnet_get_interface_link (lua_State *L)
{
struct interface_info *ii = getInterfaceByName(luaL_checkstring(L, 1));
if (ii == NULL)
return luaL_argerror(L, 1, "bad interface");
switch (ii->device_type)
{
case devt_ethernet:
lua_pushliteral(L, "ethernet");
return 1;
case devt_loopback:
lua_pushliteral(L, "loopback");
return 1;
case devt_p2p:
lua_pushliteral(L, "p2p");
return 1;
case devt_other:
default:
lua_pushnil(L);
return 1;
}
}
static int close_eth (lua_State *L)
{
eth_t **eth = (eth_t **) luaL_checkudata(L, 1, DNET_ETH_METATABLE);
assert(*eth != NULL);
eth_close(*eth);
*eth = NULL;
return success(L);
}
static eth_t *open_eth_cached (lua_State *L, int dnet_index, const char *device)
{
eth_t **eth;
lua_rawgeti(L, LUA_ENVIRONINDEX, ETH_CACHE_DNET_ETH);
lua_rawgeti(L, LUA_ENVIRONINDEX, ETH_CACHE_DEVICE_ETH);
lua_getfield(L, -1, device);
if (!lua_isuserdata(L, -1))
{
eth = (eth_t **) lua_newuserdata(L, sizeof(eth_t *));
*eth = eth_open(device);
if (*eth == NULL)
luaL_error(L, "unable to open dnet on ethernet interface %s", device);
luaL_getmetatable(L, DNET_ETH_METATABLE);
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
lua_setfield(L, -4, device);
lua_replace(L, -2); /* replace nil */
}
eth = (eth_t **) lua_touserdata(L, -1);
lua_pushvalue(L, dnet_index);
lua_pushvalue(L, -2); /* eth_t userdata */
lua_rawset(L, -5); /* add to ETH_CACHE_DNET_ETH */
lua_pop(L, 3); /* ETH_CACHE_DNET_ETH, ETH_CACHE_DEVICE_ETH, eth_t userdata */
return *eth;
}
static int ethernet_open (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE);
const char *interface_name = luaL_checkstring(L, 2);
struct interface_info *ii = getInterfaceByName(interface_name);
if (ii == NULL || ii->device_type != devt_ethernet)
return luaL_argerror(L, 2, "device is not valid ethernet interface");
udata->eth = open_eth_cached(L, 1, interface_name);
lua_pushvalue(L, 2);
udata->interface = luaL_ref(L, LUA_REGISTRYINDEX);
return success(L);
}
static int ethernet_close (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE);
udata->eth = NULL;
lua_rawgeti(L, LUA_ENVIRONINDEX, ETH_CACHE_DNET_ETH);
lua_pushvalue(L, 1);
lua_pushnil(L);
lua_rawset(L, -3);
luaL_unref(L, LUA_REGISTRYINDEX, udata->interface);
udata->interface = LUA_NOREF;
return success(L);
}
static int ethernet_send (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE);
if (udata->eth == NULL)
return luaL_error(L, "dnet ethernet interface is not open");
eth_send(udata->eth, luaL_checkstring(L, 2), lua_objlen(L, 2));
return success(L);
}
static int ip_open (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE);
udata->sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (udata->sock == -1)
return luaL_error(L, "failed to open raw socket: %s (errno %d)",
socket_strerror(socket_errno()), socket_errno());
broadcast_socket(udata->sock);
#ifndef WIN32
sethdrinclude(udata->sock);
#endif
return success(L);
}
static int ip_close (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE);
if (udata->sock == -1)
return safe_error(L, "raw socket already closed");
close(udata->sock);
udata->sock = -1;
return success(L);
}
static int ip_send (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE);
const char *packet = luaL_checkstring(L, 2);
int ret;
if (udata->sock == -1)
return luaL_error(L, "raw socket not open to send");
if (lua_objlen(L, 2) < sizeof(struct ip))
return luaL_error(L, "ip packet too short");
if (o.sendpref & PACKET_SEND_ETH)
{
struct route_nfo route;
struct sockaddr_storage srcss, dstss, *nexthop;
struct sockaddr_in *srcsin = (struct sockaddr_in *) &srcss;
struct sockaddr_in *dstsin = (struct sockaddr_in *) &dstss;
struct ip *ip = (struct ip *) packet;
u8 dstmac[6];
eth_nfo eth;
/* build sockaddr for target from user packet and determine route */
memset(&dstss, 0, sizeof(dstss));
dstsin->sin_family = AF_INET;
dstsin->sin_addr.s_addr = ip->ip_dst.s_addr;
if (!nmap_route_dst(&dstss, &route))
goto usesock;
if (route.ii.device_type != devt_ethernet)
goto usesock;
/* above we fallback to using the raw socket if we can't find an (ethernet)
* route to the host. From here on out it's ethernet all the way.
*/
/* build sockaddr for source from user packet to determine next hop mac */
memset(&srcss, 0, sizeof(srcss));
srcsin->sin_family = AF_INET;
srcsin->sin_addr.s_addr = ip->ip_src.s_addr;
if (route.direct_connect)
nexthop = &dstss;
else
nexthop = &route.nexthop;
if (!getNextHopMAC(route.ii.devfullname, route.ii.mac, &srcss, nexthop, dstmac))
return luaL_error(L, "failed to determine next hop MAC address");
/* Use cached ethernet device, and use udata's eth and interface to keep
* track of if we're reusing the same device from the previous packet, and
* close the cached device if not.
*/
memset(&eth, 0, sizeof(eth));
memcpy(eth.srcmac, route.ii.mac, sizeof(eth.srcmac));
memcpy(eth.dstmac, dstmac, sizeof(eth.dstmac));
/* close any current ethernet associated with this userdata */
lua_pushcfunction(L, ethernet_close);
lua_pushvalue(L, 1);
lua_call(L, 1, 0);
udata->eth = eth.ethsd = open_eth_cached(L, 1, route.ii.devname);
lua_pushstring(L, route.ii.devname);
udata->interface = luaL_ref(L, LUA_REGISTRYINDEX);
ret = send_ip_packet(udata->sock, &eth, (u8 *) packet, lua_objlen(L, 2));
} else {
usesock:
#ifdef WIN32
if (strlen(dev) > 0)
win32_warn_raw_sockets(dev);
#endif
ret = send_ip_packet(udata->sock, NULL, (u8 *) packet, lua_objlen(L, 2));
}
if (ret == -1)
return luaL_error(L, "error while sending: %s (errno %d)",
socket_strerror(socket_errno()), socket_errno());
return success(L);
}
static int gc (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE);
lua_pushcfunction(L, ip_close);
lua_pushvalue(L, 1);
lua_call(L, 1, 0);
lua_pushcfunction(L, ethernet_close);
lua_pushvalue(L, 1);
lua_call(L, 1, 0);
luaL_unref(L, LUA_REGISTRYINDEX, udata->interface);
return 0;
}
LUALIB_API int luaopen_dnet (lua_State *L)
{
static const luaL_reg l_dnet[] = {
{"ethernet_open", ethernet_open},
{"ethernet_close", ethernet_close},
{"ethernet_send", ethernet_send},
{"ip_open", ip_open},
{"ip_close", ip_close},
{"ip_send", ip_send},
{NULL, NULL}
};
lua_createtable(L, 2, 0);
lua_replace(L, LUA_ENVIRONINDEX);
weak_table(L, 0, 0, "k"); /* dnet udata weak, eth device strong */
lua_rawseti(L, LUA_ENVIRONINDEX, ETH_CACHE_DNET_ETH);
weak_table(L, 0, 0, "v"); /* eth_device weak */
lua_rawseti(L, LUA_ENVIRONINDEX, ETH_CACHE_DEVICE_ETH);
luaL_newmetatable(L, DNET_METATABLE);
lua_createtable(L, 0, 5);
luaL_register(L, NULL, l_dnet);
lua_setfield(L, -2, "__index");
lua_newtable(L);
lua_setfield(L, -2, "__metatable");
lua_pushcfunction(L, gc);
lua_setfield(L, -2, "__gc");
luaL_newmetatable(L, DNET_ETH_METATABLE);
lua_pushcfunction(L, close_eth);
lua_setfield(L, -2, "__gc");
return 0;
}

8
nse_dnet.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef NMAP_LUA_DNET_H
#define NMAP_LUA_DNET_H
LUALIB_API int l_dnet_new (lua_State *);
LUALIB_API int l_dnet_get_interface_link (lua_State *);
LUALIB_API int luaopen_dnet (lua_State *L);
#endif

View File

@@ -1,15 +1,3 @@
#include "nse_main.h"
#include "nse_fs.h"
#include "nse_nsock.h"
#include "nse_nmaplib.h"
#include "nse_bit.h"
#include "nse_binlib.h"
#include "nse_pcrelib.h"
#include "nse_openssl.h"
#include "nse_debug.h"
#include "nmap.h"
#include "nmap_error.h"
#include "portlist.h"
@@ -19,6 +7,17 @@
#include "Target.h"
#include "nmap_tty.h"
#include "nse_main.h"
#include "nse_utility.h"
#include "nse_fs.h"
#include "nse_nsock.h"
#include "nse_nmaplib.h"
#include "nse_bit.h"
#include "nse_binlib.h"
#include "nse_pcrelib.h"
#include "nse_openssl.h"
#include "nse_debug.h"
#define NSE_MAIN "NSE_MAIN" /* the main function */
#define NSE_TRACEBACK "NSE_TRACEBACK"
@@ -130,7 +129,8 @@ static int host_set_output (lua_State *L)
static int port_set_output (lua_State *L)
{
Port port, *p;
Port *p;
Port port;
ScriptResult sr;
Target *target = get_target(L, 1);
p = get_port(L, target, &port, 2);
@@ -406,6 +406,9 @@ static int run_main (lua_State *L)
return 0;
}
/* Lua 5.2 compatibility macro */
#define lua_yieldk(L,n,ctx,k) lua_yield(L,n)
/* int nse_yield (lua_State *L) [-?, +?, e]
*
* This function will yield the running thread back to NSE, even across script
@@ -413,12 +416,12 @@ static int run_main (lua_State *L)
* correct and only way to call is as a tail call:
* return nse_yield(L);
*/
int nse_yield (lua_State *L)
int nse_yield (lua_State *L, int ctx, lua_CFunction k)
{
lua_getfield(L, LUA_REGISTRYINDEX, NSE_YIELD);
lua_pushthread(L);
lua_call(L, 1, 1); /* returns NSE_YIELD_VALUE */
return lua_yield(L, 1); /* yield with NSE_YIELD_VALUE */
return lua_yieldk(L, 1, ctx, k); /* yield with NSE_YIELD_VALUE */
}
/* void nse_restore (lua_State *L, int number) [-, -, e]
@@ -527,7 +530,14 @@ void open_nse (void)
fatal("%s: failed to open a Lua state!", SCRIPT_ENGINE);
lua_atpanic(L_NSE, panic);
if (lua_cpcall(L_NSE, init_main, (void *) &o.chosenScripts) != 0)
#if 0
/* Lua 5.2 */
lua_pushcfunction(L_NSE, init_main);
lua_pushlightuserdata(L_NSE, &o.chosenScripts);
if (lua_pcall(L_NSE, 1, 0, 0))
#else
if (lua_cpcall(L_NSE, init_main, &o.chosenScripts))
#endif
fatal("%s: failed to initialize the script engine:\n%s\n", SCRIPT_ENGINE,
lua_tostring(L_NSE, -1));
}
@@ -540,7 +550,14 @@ void script_scan (std::vector<Target *> &targets, stype scantype)
assert(L_NSE != NULL);
lua_settop(L_NSE, 0); /* clear the stack */
if (lua_cpcall(L_NSE, run_main, (void *) &targets) != 0)
#if 0
/* Lua 5.2 */
lua_pushcfunction(L_NSE, run_main);
lua_pushlightuserdata(L_NSE, &targets);
if (lua_pcall(L_NSE, 1, 0, 0))
#else
if (lua_cpcall(L_NSE, run_main, &targets))
#endif
error("%s: Script Engine Scan Aborted.\nAn error was thrown by the "
"engine: %s", SCRIPT_ENGINE, lua_tostring(L_NSE, -1));
}

View File

@@ -9,8 +9,8 @@
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "lualib.h"
}
#include "nmap.h"
@@ -38,7 +38,7 @@ class Target;
/* API */
int nse_yield (lua_State *);
int nse_yield (lua_State *, int, lua_CFunction);
void nse_restore (lua_State *, int);
void nse_destructor (lua_State *, char);
void nse_base (lua_State *);

View File

@@ -2,6 +2,7 @@
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
#include <math.h>
@@ -19,37 +20,15 @@ extern "C" {
#include "protocols.h"
#include "nse_nmaplib.h"
#include "nse_utility.h"
#include "nse_nsock.h"
#include "nse_dnet.h"
extern NmapOps o;
static const char *NSE_PROTOCOL_OP[] = {"tcp", "udp", "sctp", NULL};
static const int NSE_PROTOCOL[] = {IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP};
static void setsfield (lua_State *L, int idx, const char *field, const char *what)
{
lua_pushvalue(L, idx);
lua_pushstring(L, what); /* what can be NULL */
lua_setfield(L, -2, field);
lua_pop(L, 1);
}
static void setnfield (lua_State *L, int idx, const char *field, lua_Number n)
{
lua_pushvalue(L, idx);
lua_pushnumber(L, n);
lua_setfield(L, -2, field);
lua_pop(L, 1);
}
static void setbfield (lua_State *L, int idx, const char *field, int b)
{
lua_pushvalue(L, idx);
lua_pushboolean(L, b);
lua_setfield(L, -2, field);
lua_pop(L, 1);
}
void set_version (lua_State *L, const struct serviceDeductions *sd)
{
setsfield(L, -1, "name", sd->name);
@@ -245,7 +224,7 @@ static int aux_mutex (lua_State *L)
}
lua_pushthread(L);
lua_rawseti(L, lua_upvalueindex(1), lua_objlen(L, lua_upvalueindex(1))+1);
return nse_yield(L);
return nse_yield(L, 0, NULL);
case DONE:
lua_pushthread(L);
if (!lua_equal(L, -1, lua_upvalueindex(2)))
@@ -341,7 +320,7 @@ static int aux_condvar (lua_State *L)
case WAIT:
lua_pushthread(L);
lua_rawseti(L, lua_upvalueindex(1), lua_objlen(L, lua_upvalueindex(1))+1);
return nse_yield(L);
return nse_yield(L, 0, NULL);
case SIGNAL:
n = lua_objlen(L, lua_upvalueindex(1));
break;
@@ -398,56 +377,6 @@ static int l_condvar (lua_State *L)
return 1; // condition variable closure
}
Target *get_target (lua_State *L, int index)
{
int top = lua_gettop(L);
Target *target;
luaL_checktype(L, index, LUA_TTABLE);
lua_getfield(L, index, "targetname");
lua_getfield(L, index, "ip");
if (!(lua_isstring(L, -2) || lua_isstring(L, -1)))
luaL_error(L, "host table does not have a 'ip' or 'targetname' field");
if (lua_isstring(L, -2)) /* targetname */
{
nse_gettarget(L, -2); /* use targetname */
if (lua_islightuserdata(L, -1))
goto done;
else
lua_pop(L, 1);
}
if (lua_isstring(L, -1)) /* ip */
nse_gettarget(L, -1); /* use ip */
if (!lua_islightuserdata(L, -1))
luaL_argerror(L, 1, "host is not being processed right now");
done:
target = (Target *) lua_touserdata(L, -1);
lua_settop(L, top); /* reset stack */
return target;
}
Port *get_port (lua_State *L, Target *target, Port *port, int index)
{
Port *p = NULL;
int portno, protocol;
luaL_checktype(L, index, LUA_TTABLE);
lua_getfield(L, index, "number");
if (!lua_isnumber(L, -1))
luaL_error(L, "port 'number' field must be a number");
lua_getfield(L, index, "protocol");
if (!lua_isstring(L, -1))
luaL_error(L, "port 'protocol' field must be a string");
portno = (int) lua_tointeger(L, -2);
protocol = strcmp(lua_tostring(L, -1), "tcp") == 0 ? IPPROTO_TCP :
strcmp(lua_tostring(L, -1), "udp") == 0 ? IPPROTO_UDP :
strcmp(lua_tostring(L, -1), "sctp") == 0 ? IPPROTO_SCTP :
luaL_error(L, "port 'protocol' field must be \"udp\", \"sctp\" or \"tcp\"");
while ((p = target->ports.nextPort(p, port, protocol, PORT_UNKNOWN)) != NULL)
if (p->portno == portno)
break;
lua_pop(L, 2);
return p;
}
/* Generates an array of port data for the given host and leaves it on
* the top of the stack
*/
@@ -457,7 +386,8 @@ static int l_get_ports (lua_State *L)
"open|filtered", "closed|filtered", NULL};
static const int states[] = {PORT_OPEN, PORT_FILTERED, PORT_UNFILTERED,
PORT_CLOSED, PORT_OPENFILTERED, PORT_CLOSEDFILTERED};
Port *p = NULL, port;
Port *p = NULL;
Port port; /* dummy Port for nextPort */
Target *target = get_target(L, 1);
int protocol = NSE_PROTOCOL[luaL_checkoption(L, 3, NULL, NSE_PROTOCOL_OP)];
int state = states[luaL_checkoption(L, 4, NULL, state_op)];
@@ -487,7 +417,8 @@ static int l_get_ports (lua_State *L)
static int l_get_port_state (lua_State *L)
{
Target *target;
Port port, *p;
Port *p;
Port port; /* dummy Port */
target = get_target(L, 1);
p = get_port(L, target, &port, 2);
if (p == NULL)
@@ -522,7 +453,8 @@ static int l_set_port_state (lua_State *L)
static const int opstate[] = {PORT_OPEN, PORT_CLOSED};
static const char *op[] = {"open", "closed", NULL};
Target *target;
Port port, *p;
Port *p;
Port port;
target = get_target(L, 1);
if ((p = get_port(L, target, &port, 2)) != NULL)
{
@@ -561,7 +493,8 @@ static int l_set_port_version (lua_State *L)
"incomplete"
};
Target *target;
Port port, *p;
Port *p;
Port port;
enum service_tunnel_type tunnel = SERVICE_TUNNEL_NONE;
enum serviceprobestate probestate =
opversion[luaL_checkoption(L, 3, "hardmatched", ops)];
@@ -839,19 +772,11 @@ int luaopen_nmap (lua_State *L)
lua_settop(L, 0); // clear stack
luaL_register(L, "nmap", nmaplib);
lua_newtable(L);
lua_createtable(L, 0, 1);
lua_pushliteral(L, "v");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2); // Allow closures to be collected (see l_mutex)
weak_table(L, 0, 0, "v"); /* allow closures to be collected (see l_mutex) */
lua_pushcclosure(L, l_mutex, 1); /* mutex function */
lua_setfield(L, -2, "mutex");
lua_newtable(L);
lua_createtable(L, 0, 1);
lua_pushliteral(L, "v");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2); // Allow closures to be collected (see l_condvar)
weak_table(L, 0, 0, "v"); /* allow closures to be collected (see l_condvar) */
lua_pushcclosure(L, l_condvar, 1); // condvar function
lua_setfield(L, -2, "condvar");
@@ -862,6 +787,10 @@ int luaopen_nmap (lua_State *L)
lua_pushliteral(L, "nsock");
lua_call(L, 1, 0);
lua_pushcclosure(L, luaopen_dnet, 0);
lua_pushliteral(L, "dnet");
lua_call(L, 1, 0);
lua_settop(L, 1); // just nmap lib on stack
return 1;

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,11 @@
#ifndef NMAP_LUA_NSOCK_H
#define NMAP_LUA_NSOCK_H
int luaopen_nsock(lua_State *);
int l_nsock_new(lua_State *);
int l_nsock_sleep(lua_State *L);
#include "nse_main.h"
int l_dnet_new(lua_State *);
int l_dnet_get_interface_link(lua_State *);
LUALIB_API int luaopen_nsock (lua_State *);
LUALIB_API int l_nsock_new (lua_State *);
LUALIB_API int l_nsock_sleep (lua_State *);
#define NSE_NSOCK_LOOP "NSOCK_LOOP"

180
nse_utility.cc Normal file
View File

@@ -0,0 +1,180 @@
#include <stdlib.h>
#include "Target.h"
#include "portlist.h"
#include "nse_main.h"
#include "nse_utility.h"
/* size_t table_length (lua_State *L, int index)
*
* Returns the length of the table at index index.
* This length is the number of elements, not just array elements.
*/
size_t table_length (lua_State *L, int index)
{
size_t len = 0;
lua_pushvalue(L, index);
lua_pushnil(L);
while (lua_next(L, -2))
{
len++;
lua_pop(L, 1);
}
lua_pop(L, 1);
return len;
}
void setsfield (lua_State *L, int idx, const char *field, const char *what)
{
lua_pushvalue(L, idx);
lua_pushstring(L, what); /* what can be NULL */
lua_setfield(L, -2, field);
lua_pop(L, 1);
}
void setnfield (lua_State *L, int idx, const char *field, lua_Number n)
{
lua_pushvalue(L, idx);
lua_pushnumber(L, n);
lua_setfield(L, -2, field);
lua_pop(L, 1);
}
void setbfield (lua_State *L, int idx, const char *field, int b)
{
lua_pushvalue(L, idx);
lua_pushboolean(L, b);
lua_setfield(L, -2, field);
lua_pop(L, 1);
}
int success (lua_State *L)
{
lua_pushboolean(L, true);
return 1;
}
int safe_error (lua_State *L, const char *message)
{
lua_pushboolean(L, false);
lua_pushstring(L, message);
return 2;
}
void weak_table (lua_State *L, int narr, int nrec, const char *mode)
{
lua_createtable(L, narr, nrec);
lua_createtable(L, 0, 1);
lua_pushstring(L, mode);
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
}
/* const char *check_target (lua_State *L, int idx)
*
* Check for a valid target specification at index idx.
* This function checks for a string at idx or a table containing
* the typical host table fields, 'ip' and 'targetname' in particular.
*/
void check_target (lua_State *L, int idx, const char **address, const char **targetname)
{
if (lua_istable(L, idx)) {
lua_getfield(L, idx, "ip");
*address = lua_tostring(L, -1);
lua_getfield(L, idx, "targetname");
*targetname = lua_tostring(L, -1);
if (address == NULL && targetname == NULL)
luaL_argerror(L, idx, "host table lacks 'ip' or 'targetname' fields");
*address = *address ? *address : *targetname;
lua_pop(L, 2); /* no point replacing idx, need 2 only have 1 */
} else {
*address = *targetname = luaL_checkstring(L, idx);
}
}
/* unsigned short check_port (lua_State *L, int idx)
*
* Check for a valid port specification at index idx.
*/
unsigned short check_port (lua_State *L, int idx, const char **protocol)
{
unsigned short port;
if (lua_istable(L, idx)) {
lua_getfield(L, idx, "number");
if (!lua_isnumber(L, -1))
luaL_argerror(L, idx, "port table lacks numeric 'number' field");
port = (unsigned short) lua_tointeger(L, -1);
lua_getfield(L, idx, "protocol");
*protocol = lua_tostring(L, -1);
lua_pop(L, 2);
} else {
port = (unsigned short) luaL_checkint(L, idx);
}
return port;
}
/* Target *get_target (lua_State *L, int index)
*
* This function checks the value at index for a valid host table. It locates
* the associated Target (C++) class object associated with the host and
* returns it. If the Target is not being scanned then an error will be raised.
*/
Target *get_target (lua_State *L, int index)
{
int top = lua_gettop(L);
Target *target;
luaL_checktype(L, index, LUA_TTABLE);
lua_getfield(L, index, "targetname");
lua_getfield(L, index, "ip");
if (!(lua_isstring(L, -2) || lua_isstring(L, -1)))
luaL_error(L, "host table does not have a 'ip' or 'targetname' field");
if (lua_isstring(L, -2)) /* targetname */
{
nse_gettarget(L, -2); /* use targetname */
if (lua_islightuserdata(L, -1))
goto done;
else
lua_pop(L, 1);
}
if (lua_isstring(L, -1)) /* ip */
nse_gettarget(L, -1); /* use ip */
if (!lua_islightuserdata(L, -1))
luaL_argerror(L, 1, "host is not being processed right now");
done:
target = (Target *) lua_touserdata(L, -1);
lua_settop(L, top); /* reset stack */
return target;
}
/* Target *get_port (lua_State *L, Target *target, Port *port, int index)
*
* This function checks the value at index for a valid port table. It locates
* the associated Port (C++) class object associated with the host and
* returns it.
*/
Port *get_port (lua_State *L, Target *target, Port *port, int index)
{
Port *p = NULL;
int portno, protocol;
luaL_checktype(L, index, LUA_TTABLE);
lua_getfield(L, index, "number");
if (!lua_isnumber(L, -1))
luaL_error(L, "port 'number' field must be a number");
lua_getfield(L, index, "protocol");
if (!lua_isstring(L, -1))
luaL_error(L, "port 'protocol' field must be a string");
portno = (int) lua_tointeger(L, -2);
protocol = strcmp(lua_tostring(L, -1), "tcp") == 0 ? IPPROTO_TCP :
strcmp(lua_tostring(L, -1), "udp") == 0 ? IPPROTO_UDP :
strcmp(lua_tostring(L, -1), "sctp") == 0 ? IPPROTO_SCTP :
luaL_error(L, "port 'protocol' field must be \"udp\", \"sctp\" or \"tcp\"");
while ((p = target->ports.nextPort(p, port, protocol, PORT_UNKNOWN)) != NULL)
if (p->portno == portno)
break;
lua_pop(L, 2);
return p;
}

19
nse_utility.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef NMAP_NSE_UTILITY_H
#define NMAP_NSE_UTILITY_H
size_t table_length (lua_State *, int);
void setsfield (lua_State *, int, const char *, const char *);
void setnfield (lua_State *, int, const char *, lua_Number);
void setbfield (lua_State *, int, const char *, int);
void weak_table (lua_State *, int, int, const char *);
int success (lua_State *);
int safe_error (lua_State *, const char *);
void check_target (lua_State *, int, const char **, const char **);
unsigned short check_port (lua_State *, int, const char **);
Target *get_target (lua_State *, int);
Port *get_port (lua_State *, Target *, Port *, int);
#endif

View File

@@ -35,12 +35,6 @@ request_types_str[6] = "DHCPNAK"
request_types_str[7] = "DHCPRELEASE"
request_types_str[8] = "DHCPINFORM"
-- This pulls back 4 bytes in the packet that correspond to the transaction id. This should be randomly
-- generated and different for every instance of a script (to prevent collisions)
pcap_callback = function(packetsz, layer2, layer3)
return string.sub(layer3, 33, 36)
end
---Read an IP address or a list of IP addresses. Print an error if the length isn't a multiple of 4.
--
--@param data The packet.
@@ -369,9 +363,8 @@ local function dhcp_send(interface, host, packet, transaction_id)
-- Create a pcap socket to listen for the response. I used to consider this a hack, but
-- it really isn't -- it's kinda how this has to be done.
local pcap = nmap.new_socket()
pcap:pcap_open(interface, 590, 0, pcap_callback, "udp port 68")
pcap:pcap_open(interface, 590, false, "udp port 68")
pcap:set_timeout(5000)
pcap:pcap_register(transaction_id)
stdnse.print_debug(1, "dhcp: Starting listener")
-- Create the UDP socket (TODO: enable SO_BROADCAST if we need to)
@@ -386,7 +379,12 @@ local function dhcp_send(interface, host, packet, transaction_id)
socket:send(packet)
-- Read the response
local status, err, _, data = pcap:pcap_receive()
local status, length, layer2, layer3 = pcap:pcap_receive();
-- This pulls back 4 bytes in the packet that correspond to the transaction id. This should be randomly
-- generated and different for every instance of a script (to prevent collisions)
while status and layer3:sub(33, 36) ~= transaction_id do
status, length, layer2, layer3 = pcap:pcap_receive();
end
if(status == false) then
stdnse.print_debug(1, "dhcp: Error calling pcap_receive(): %s", err)
return false, "Error calling pcap_receive(): " .. err

View File

@@ -483,8 +483,7 @@ function receive_bytes(n)
-- delimiter string (or matches the function passed in). This function
-- continues to read from the network until the delimiter is found or the
-- function times out. If data is read beyond the delimiter, that data is
-- saved in a buffer for the next call to <code>receive_buf</code>. This
-- buffer is cleared on subsequent calls to other Network I/O API functions.
-- saved in a buffer for the next call to <code>receive_buf</code>.
--
-- The first argument may be either a pattern or a function. If a pattern, that
-- pattern is used to separate the data. If a function, it must take exactly
@@ -501,12 +500,7 @@ function receive_bytes(n)
--
-- On success the function returns true along with the received data. On failure
-- the function returns <code>false</code> or <code>nil</code> along with an
-- error string. Possible error messages are the same as those that the other
-- receive functions can return, with the addition of
-- * <code>"Error inside splitting-function"</code>: The first argument was a function which caused an error while being called.
-- * <code>"Error in string.find (nsockobj:receive_buf)!"</code>: A string was provided as the first argument, and string.find() yielded an error while being called.
-- * <code>"Expected either a function or a string!"</code>: The first argument was neither a function nor a string.
-- * <code>"Delimiter has negative size!"</code>: The returned start offset is greater than the end offset.
-- receive error string. This function may also throw errors for incorrect usage.
-- @param delimiter A Lua pattern or a function with return values like those of
-- <code>string.find</code>.
-- @param keeppattern Whether to return the delimiter string with any returned
@@ -565,37 +559,19 @@ function set_timeout(t)
--- Opens a socket for raw packet capture.
--
-- The callback function is a function that receives a packet with headers and
-- computes a "packet hash", some value derived from the packet. For example,
-- the callback function could extract the source IP address from a packet. The
-- hash of each packet received is compared against all the strings registered
-- with the <code>pcap_register</code> function.
-- @param device The dnet-style interface name of the device you want to capture
-- from.
-- @param snaplen The length of each packet you want to capture (similar to the
-- <code>-s</code> option to tcpdump)
-- @param promisc Set to 1 if the interface should activate promiscuous mode,
-- and 0 otherwise.
-- @param test_function Callback function used to compute the packet hash.
-- @param bpf A string describing a Berkeley Packet Filter expression (like
-- those provided to tcpdump).
-- @see new_socket, pcap_register, pcap_receive
-- @see new_socket, pcap_receive
-- @usage
-- local socket = nmap.new_socket()
-- socket:pcap_open("eth0", 64, 0, callback, "tcp")
function pcap_open(device, snaplen, promisc, test_function, bpf)
--- Starts listening for incoming packets.
--
-- The provided <code>packet_hash</code> is a binary string which has to match
-- the hash returned by the <code>test_function</code> parameter provided to
-- <code>pcap_open</code>. If you want to receive all packets, just provide
-- the empty string (<code>""</code>). There has to be a call to
-- <code>pcap_register</code> before a call to <code>pcap_receive</code>.
-- @param packet_hash A binary string that is compared against packet hashes.
-- @see pcap_open, pcap_receive
-- @usage socket:pcap_register("")
function pcap_register(packet_hash)
-- socket:pcap_open("eth0", 64, false, "tcp")
function pcap_open(device, snaplen, promisc, bpf)
--- Receives a captured packet.
--
@@ -609,7 +585,7 @@ function pcap_register(packet_hash)
-- @return Data from the second OSI layer (e.g. ethernet headers).
-- @return Data from the third OSI layer (e.g. IPv4 headers).
-- @return Packet capture time, as floating point seconds since the epoch
-- @see pcap_open, pcap_register
-- @see pcap_open
-- @usage status, plen, l2_data, l3_data, time = socket:pcap_receive()
function pcap_receive()

View File

@@ -25,13 +25,14 @@ require "openssl"
-- @return packet_length, packet_length or nil
-- the return is similar to the lua function string:find()
check_packet_length = function( buffer )
if #buffer < 4 then return nil end
local payload_length, packet_length, offset
offset, payload_length = bin.unpack( ">I", buffer )
local padding = 8 - payload_length % 8
assert(payload_length)
packet_length = buffer:len()
if payload_length + 4 + padding > packet_length then return nil end
return packet_length, packet_length
local total = 4+payload_length+padding;
if total > #buffer then return nil end
return total, total;
end
--- Receives a complete SSH packet, even if fragmented
@@ -43,7 +44,7 @@ end
-- @return status True or false
-- @return packet The packet received
receive_ssh_packet = function( socket )
local status, packet = socket:receive_buf(check_packet_length)
local status, packet = socket:receive_buf(check_packet_length, true)
return status, packet
end
@@ -76,7 +77,7 @@ fetch_host_key = function(host, port)
padding = 8 - packet_length % 8
offset = offset + padding
if padding + packet_length + 4 == data:len() then
if padding + packet_length + 4 == #data then
-- seems to be a proper SSH1 packet
local msg_code,host_key_bits,exp,mod,length,fp_input
offset, msg_code = bin.unpack( ">c", data, offset )

View File

@@ -29,11 +29,12 @@ local SSH2
-- @return packet_length, packet_length or nil
-- the return is similar to the lua function string:find()
check_packet_length = function( buffer )
if #buffer < 4 then return nil end -- not enough data in buffer for int
local packet_length, offset
offset, packet_length = bin.unpack( ">I", buffer )
assert(packet_length)
if packet_length + 4 > buffer:len() then return nil end
return packet_length, packet_length
return packet_length+4, packet_length+4
end
--- Receives a complete SSH packet, even if fragmented
@@ -45,7 +46,7 @@ end
-- @return status True or false
-- @return packet The packet received
transport.receive_packet = function( socket )
local status, packet = socket:receive_buf(check_packet_length)
local status, packet = socket:receive_buf(check_packet_length, true)
return status, packet
end
@@ -78,7 +79,9 @@ end
-- @return Payload of the SSH-2 packet.
transport.payload = function( packet )
local packet_length, padding_length, payload_length, payload, offset
offset, packet_length, padding_length = bin.unpack( ">Ic", packet )
offset, packet_length = bin.unpack( ">I", packet )
packet = packet:sub(offset);
offset, padding_length = bin.unpack( ">c", packet )
assert(packet_length and padding_length)
payload_length = packet_length - padding_length - 1
if packet_length ~= packet:len() then

View File

@@ -84,13 +84,6 @@ local checkpkt = function(reply, orig)
return true
end
--- pcap callback
-- @return destination ip address, the ip protocol and icmp type
local callback = function(size, layer2, layer3)
local ip = packet.Packet:new(layer3, layer3:len())
return bin.pack('ACC', ip.ip_bin_dst, ip.ip_p, ip.icmp_type)
end
--- set destination port and ip ttl to a generic tcp packet
-- @param ip the ip object
-- @param dport the layer 4 destination port
@@ -274,6 +267,13 @@ local portrange = function(ports)
return stdnse.strjoin(",", strrange)
end
--- pcap check function
-- @return destination ip address, the ip protocol and icmp type
local function check (size, layer2, layer3)
local ip = packet.Packet:new(layer3, layer3:len())
return bin.pack('ACC', ip.ip_bin_dst, ip.ip_p, ip.icmp_type)
end
-- main firewalking logic
action = function(host)
local sock = nmap.new_dnet()
@@ -290,7 +290,7 @@ action = function(host)
end
-- filter for incoming icmp time exceeded replies
pcap:pcap_open(host.interface, 104, 0, callback, "icmp and dst host " .. saddr)
pcap:pcap_open(host.interface, 104, false, "icmp and dst host " .. saddr)
try(sock:ip_open())
@@ -309,11 +309,14 @@ action = function(host)
while retry < MAX_RETRIES do
try(sock:ip_send(pkt.buf))
pcap:pcap_register(bin.pack('ACC', pkt.ip_bin_src, packet.IPPROTO_ICMP, ICMP_TIME_EXCEEDED))
local status, _, _, rep = pcap:pcap_receive()
local status, length, layer2, layer3 = pcap:pcap_receive();
local test = bin.pack('ACC', pkt.ip_bin_src, packet.IPPROTO_ICMP, ICMP_TIME_EXCEEDED);
while status and test ~= check(length, layer2, layer3) do
status, length, layer2, layer3 = pcap:pcap_receive();
end
if status then
if checkpkt(rep, pkt) then
if checkpkt(layer3, pkt) then
stdnse.print_debug(1, "Firewalk: discovered fwd port " .. port)
table.insert(fwdports, port)
break

View File

@@ -33,9 +33,9 @@ require 'packet'
local NUMPROBES = 6
--- Pcap callback
--- Pcap check function
-- @return Destination and source IP addresses and TCP ports
local callback = function(size, layer2, layer3)
local function check (size, layer2, layer3)
local ip = packet.Packet:new(layer3, layer3:len())
return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport)
end
@@ -222,7 +222,7 @@ action = function(host)
try = nmap.new_try(function() sock:ip_close() end)
pcap:pcap_open(host.interface, 104, 0, callback, "tcp and dst host " .. saddr .. " and src host " .. daddr .. " and src port " .. port)
pcap:pcap_open(host.interface, 104, false, "tcp and dst host " .. saddr .. " and src host " .. daddr .. " and src port " .. port)
pcap:set_timeout(host.times.timeout * 1000)
@@ -231,12 +231,14 @@ action = function(host)
while i <= NUMPROBES do
try(sock:ip_send(tcp.buf))
pcap:pcap_register(bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport))
local status, len, _, pkt = pcap:pcap_receive()
local status, len, layer2, layer3 = pcap:pcap_receive()
local test = bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)
while status and test ~= check(len, layer2, layer3) do
status, len, layer2, layer3 = pcap:pcap_receive()
end
if status then
table.insert(ipids, packet.u16(pkt, 4))
table.insert(ipids, packet.u16(layer3, 4))
end
updatepkt(tcp)

View File

@@ -145,7 +145,7 @@ end
-- This is all we can use since we can get various protocols back from
-- different hosts
local callback = function(size, layer2, layer3)
local function check (size, layer2, layer3)
local ip = packet.Packet:new(layer3, layer3:len())
return bin.pack('A', ip.ip_bin_dst)
end
@@ -306,7 +306,7 @@ action = function(host)
try = nmap.new_try(function() sock:ip_close() end)
pcap:pcap_open(host.interface, 104, 0, callback, "dst host " .. saddr .. " and (icmp or (" .. proto .. " and src host " .. daddr .. " and src port " .. port .. "))")
pcap:pcap_open(host.interface, 104, false, "dst host " .. saddr .. " and (icmp or (" .. proto .. " and src host " .. daddr .. " and src port " .. port .. "))")
-- Since we're sending potentially large amounts of data per packet,
-- simply bump up the host's calculated timeout value. Most replies
@@ -337,12 +337,14 @@ action = function(host)
end
end
pcap:pcap_register(bin.pack('A', pkt.ip_bin_src))
status, _, _, ip = pcap:pcap_receive()
local test = bin.pack('A', pkt.ip_bin_src)
local status, length, layer2, layer3 = pcap:pcap_receive()
while status and test ~= check(length, layer2, layer3) do
status, length, layer2, layer3 = pcap:pcap_receive()
end
if status then
local t, v = checkpkt(ip, pkt)
local t, v = checkpkt(layer3, pkt)
if t == "gotreply" then
gotit = true
break

View File

@@ -157,9 +157,9 @@ local tstat = function(n1, n2, u1, u2, v1, v2)
return math.abs(u1 - u2) / math.sqrt(a * (b / dof))
end
--- Pcap callback
--- Pcap check
-- @return Destination and source IP addresses and TCP ports
local callback = function(size, layer2, layer3)
local function check (size, layer2, layer3)
local ip = packet.Packet:new(layer3, layer3:len())
return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport)
end
@@ -418,7 +418,7 @@ action = function(host)
local conf, delay, numtrips = try(getopts())
pcap:pcap_open(host.interface, 104, 0, callback, "tcp and dst host " .. saddr .. " and src host " .. daddr)
pcap:pcap_open(host.interface, 104, false, "tcp and dst host " .. saddr .. " and src host " .. daddr)
try(sock:ip_open())
@@ -452,15 +452,17 @@ action = function(host)
stats[j].fam = -1
end
pcap:pcap_register(bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport))
start = stdnse.clock_us()
try(sock:ip_send(tcp.buf))
stats[j].sent = stats[j].sent + 1
local status, len, _, pkt, stop = pcap:pcap_receive()
local test = bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)
local status, length, layer2, layer3, stop = pcap:pcap_receive()
while status and test ~= check(length, layer2, layer3) do
status, length, layer2, layer3, stop = pcap:pcap_receive()
end
if not stop then
-- probably a timeout, just grab current time

View File

@@ -24,14 +24,13 @@ hostrule = function(host)
nmap.get_interface_link(host.interface) == 'ethernet'
end
callback = function(packetsz, layer2, layer3)
local function check (packetsz, layer2, layer3)
return string.sub(layer2, 0, 12)
end
do_test = function(dnet, pcap, host, test)
local _
local status
local status, length, layer2, layer3
local i = 0
-- ARP requests are send with timeouts: 10ms, 40ms, 90ms
@@ -41,15 +40,21 @@ do_test = function(dnet, pcap, host, test)
-- flush buffers :), wait quite long.
repeat
pcap:set_timeout(100)
pcap:pcap_register(host.mac_addr_src .. host.mac_addr)
status ,_,_,_ = pcap:pcap_receive()
local test = host.mac_addr_src .. host.mac_addr
status, length, layer2, layer3 = pcap:pcap_receive()
while status and test ~= check(length, layer2, layer3) do
status, length, layer2, layer3 = pcap:pcap_receive()
end
until status ~= true
pcap:set_timeout(10 * i*i)
pcap:pcap_register(host.mac_addr_src .. host.mac_addr)
dnet:ethernet_send(test)
status ,_,_,_ = pcap:pcap_receive()
local test = host.mac_addr_src .. host.mac_addr
status, length, layer2, layer3 = pcap:pcap_receive()
while status and test ~= check(length, layer2, layer3) do
status, length, layer2, layer3 = pcap:pcap_receive()
end
if status == true then
-- the basic idea, was to inform user about time, when we got packet
-- so that 1 would mean (0-10ms), 2=(10-40ms) and 3=(40ms-90ms)
@@ -79,7 +84,7 @@ action = function(host)
}
dnet:ethernet_open(host.interface)
pcap:pcap_open(host.interface, 64, 0, callback, "arp")
pcap:pcap_open(host.interface, 64, false, "arp")
local test_static = host.mac_addr_src ..
string.char(0x08,0x06, 0x00,0x01, 0x08,0x00, 0x06,0x04, 0x00,0x01) ..