Permalink
Browse files

Include a reference in packet message

Rework and rename the packet message sent to the caller to include a
reference. Modify read to return only the packet contents.
  • Loading branch information...
1 parent 4da05ce commit 44bfe7950c9289c122eb8ae515304afc1b8e406c @msantos committed Aug 22, 2012
Showing with 119 additions and 63 deletions.
  1. +11 −11 README.md
  2. +2 −2 WINDOWS.md
  3. +51 −42 c_src/ewpcap.c
  4. +44 −0 include/ewpcap.hrl
  5. +1 −1 src/ewpcap.app.src
  6. +10 −7 src/ewpcap.erl
View
@@ -25,13 +25,11 @@ ewpcap hasn't been heavily tested. It will probably segfault.
On Ubuntu: sudo apt-get install libpcap-dev
-These libraries are not required but can be used with epcap\_compile:
+These libraries are not required but can be used with ewpcap:
* pkt: https://github.com/msantos/pkt.git
- Use pkt to map the datalinktype to a number:
-
- pkt:dlt(en10mb)
+ Use pkt to decode/encode packets read from the network.
* privileges
@@ -104,7 +102,9 @@ SMP erlang must be enabled (erl -smp -pa ebin).
Begin sniffing the network. Packets are returned as messages to
the caller:
- {packet, DatalinkType, Time, Length, Packet}
+ {ewpcap, Ref, DatalinkType, Time, Length, Packet}
+
+ Ref is a reference identifying the socket handle.
The DataLinkType is an integer representing the link layer,
e.g., ethernet, Linux cooked socket.
@@ -123,9 +123,10 @@ SMP erlang must be enabled (erl -smp -pa ebin).
Types Socket = resource()
Timeout = uint() | infinity
- Packet = packet()
+ Packet = binary()
- Convenience function wrapping receive.
+ Convenience function wrapping receive, returning the packet
+ contents.
write(Socket) -> ok | {error, pcap_error_string()}
@@ -159,9 +160,8 @@ SMP erlang must be enabled (erl -smp -pa ebin).
* rename loop/1
-* should packet message contain a reference?
+* ewpcap, epcap, epcap\_compile ... confusing!
-* possible/necessary/safe for multiple ewpcap\_loop threads to be running?
- * pcap\_breakloop affects all threads
+* pcap\_sendpacket may block
-* ewpcap, epcap, epcap\_compile ... confusing!
+* re-write as a port driver?
View
@@ -10,7 +10,7 @@ This document describes what tools are needed and how they should be set up to b
* **Cygwin** is needed to get an usable console on Windows. The developer tools such as make and git should be included in the installation (it needs to be selected during the installation as it is not by default).
-* **Erland/OTP** the binary package for Windows is available. Version used: R15B01
+* **Erlang/OTP** the binary package for Windows is available. Version used: R15B01
* **Git** is used for cloning rebar and ewpcap from Github.
@@ -68,4 +68,4 @@ It should be possible to build the project with the `make` command now.
### Using ewpcap
-* TODO
+* TODO
View
@@ -36,29 +36,27 @@
#include "erl_driver.h"
typedef struct _ewpcap_state {
+ ErlNifEnv *env;
ErlNifPid pid;
ErlNifTid tid;
+ ERL_NIF_TERM ref;
pcap_t *p;
int datalink;
} EWPCAP_STATE;
-typedef struct _ewpcap_cb {
- ErlNifEnv *env;
- EWPCAP_STATE *state;
-} EWPCAP_CB;
-
ErlNifResourceType *EWPCAP_RESOURCE;
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_error;
static ERL_NIF_TERM atom_enomem;
-static ERL_NIF_TERM atom_packet;
+static ERL_NIF_TERM atom_ewpcap;
+static ERL_NIF_TERM atom_ewpcap_resource;
static ERL_NIF_TERM atom_ewpcap_error;
void *ewpcap_loop(void *arg);
void ewpcap_cleanup(ErlNifEnv *env, void *obj);
void ewpcap_send(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes);
-void ewpcap_error(EWPCAP_CB *cb, char *msg);
+void ewpcap_error(EWPCAP_STATE *ep, char *msg);
static int
@@ -74,7 +72,8 @@ load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info)
atom_ok = enif_make_atom(env, "ok");
atom_error = enif_make_atom(env, "error");
atom_enomem = enif_make_atom(env, "enomem");
- atom_packet = enif_make_atom(env, "packet");
+ atom_ewpcap = enif_make_atom(env, "ewpcap");
+ atom_ewpcap_resource = enif_make_atom(env, "ewpcap_resource");
atom_ewpcap_error = enif_make_atom(env, "ewpcap_error");
if ( (EWPCAP_RESOURCE = enif_open_resource_type(env, NULL,
@@ -89,70 +88,62 @@ load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info)
ewpcap_loop(void *arg)
{
EWPCAP_STATE *ep = arg;
- EWPCAP_CB cb = {0};
- ErlNifEnv *env = NULL;
int rv = 0;
- env = enif_alloc_env();
- if (env == NULL)
- goto ERR;
-
- cb.env = env;
- cb.state = ep;
-
- rv = pcap_loop(ep->p, -1 /* loop forever */, ewpcap_send, (u_char *)&cb);
+ rv = pcap_loop(ep->p, -1 /* loop forever */, ewpcap_send, (u_char *)ep);
switch (rv) {
case -2:
/* break requested using pcap_breakloop */
break;
case -1:
- ewpcap_error(&cb, pcap_geterr(ep->p));
+ ewpcap_error(ep, pcap_geterr(ep->p));
break;
default:
/* XXX shouldn't reach this */
break;
}
-ERR:
- if (env)
- enif_free_env(env);
+ enif_free_env(ep->env);
return NULL;
}
void
ewpcap_send(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
- EWPCAP_CB *cb = (EWPCAP_CB *)user;
- ErlNifEnv *env = NULL;
- EWPCAP_STATE *ep = NULL;
+ EWPCAP_STATE *ep = (EWPCAP_STATE *)user;
ErlNifBinary buf = {0};
+ ErlNifEnv *env = NULL;
- env = cb->env;
- ep = cb->state;
-
/* XXX no way to indicate an error? */
if (ep->p == NULL)
return;
+ env = enif_alloc_env();
+ if (env == NULL) {
+ pcap_breakloop(ep->p);
+ return;
+ }
+
if (!enif_alloc_binary(h->caplen, &buf)) {
pcap_breakloop(ep->p);
return;
}
(void)memcpy(buf.data, bytes, buf.size);
- /* {packet, DatalinkType, Time, ActualLength, Packet} */
+ /* {ewpcap, Ref, DatalinkType, Time, ActualLength, Packet} */
(void)enif_send(
NULL,
&ep->pid,
env,
- enif_make_tuple5(env,
- atom_packet,
+ enif_make_tuple6(env,
+ atom_ewpcap,
+ enif_make_copy(env, ep->ref),
enif_make_int(env, ep->datalink),
enif_make_tuple3(env,
enif_make_ulong(env, abs(h->ts.tv_sec / 1000000)),
@@ -164,34 +155,36 @@ ewpcap_send(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
)
);
- enif_clear_env(env);
+ enif_free_env(env);
}
void
-ewpcap_error(EWPCAP_CB *cb, char *msg)
+ewpcap_error(EWPCAP_STATE *ep, char *msg)
{
ErlNifEnv *env = NULL;
- EWPCAP_STATE *ep = NULL;
-
-
- env = cb->env;
- ep = cb->state;
if (ep->p == NULL)
return;
- /* {ewpcap_error, Error} */
+ env = enif_alloc_env();
+ if (env == NULL) {
+ pcap_breakloop(ep->p);
+ return;
+ }
+
+ /* {ewpcap_error, Ref, Error} */
(void)enif_send(
NULL,
&ep->pid,
env,
- enif_make_tuple2(env,
+ enif_make_tuple3(env,
atom_ewpcap_error,
+ enif_make_copy(env, ep->ref),
enif_make_string(env, msg, ERL_NIF_LATIN1)
)
);
- enif_clear_env(env);
+ enif_free_env(env);
}
@@ -206,6 +199,7 @@ nif_pcap_open_live(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
EWPCAP_STATE *ep = NULL;
ERL_NIF_TERM res = {0};
+ ERL_NIF_TERM ref = {0};
if (!enif_inspect_iolist_as_binary(env, argv[0], &device))
@@ -231,6 +225,7 @@ nif_pcap_open_live(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
if (ep == NULL)
return enif_make_tuple2(env, atom_error, atom_enomem);
+
/* "any" is a Linux only */
ep->p = pcap_open_live((device.size == 0 ? "any" : (char *)device.data),
snaplen, promisc, to_ms, errbuf);
@@ -243,10 +238,24 @@ nif_pcap_open_live(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[])
ep->datalink = pcap_datalink(ep->p);
(void)enif_self(env, &ep->pid);
+ ep->env = enif_alloc_env();
+ if (ep->env == NULL) {
+ pcap_close(ep->p);
+ return enif_make_tuple2(env, atom_error, atom_enomem);
+ }
+
+ ep->ref = enif_make_ref(ep->env);
+ ref = enif_make_copy(env, ep->ref);
+
res = enif_make_resource(env, ep);
enif_release_resource(ep);
- return enif_make_tuple2(env, atom_ok, res);
+ return enif_make_tuple2(env,
+ atom_ok,
+ enif_make_tuple3(env,
+ atom_ewpcap_resource,
+ ref,
+ res));
}
static ERL_NIF_TERM
View
@@ -0,0 +1,44 @@
+%% Copyright (c) 2012, Michael Santos <michael.santos@gmail.com>
+%% All rights reserved.
+%%
+%% Redistribution and use in source and binary forms, with or without
+%% modification, are permitted provided that the following conditions
+%% are met:
+%%
+%% Redistributions of source code must retain the above copyright
+%% notice, this list of conditions and the following disclaimer.
+%%
+%% 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.
+%%
+%% Neither the name of the author nor the names of its contributors
+%% may be used to endorse or promote products derived from this software
+%% without specific prior written permission.
+%%
+%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+%% COPYRIGHT HOLDER 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.
+
+% Packet format in message
+-record(ewpcap, {
+ ref, % socket identifier
+ dlt, % datalink type
+ time, % time: {MegaSecs, Secs, MicroSecs}
+ length, % actual packet length (data may be truncated)
+ packet % packet
+ }).
+
+-record(ewpcap_resource, {
+ ref,
+ res
+ }).
View
@@ -1,3 +1,3 @@
{application, ewpcap,
[{description, "PCAP NIF interface"},
- {vsn, "0.01"}]}.
+ {vsn, "0.02"}]}.
View
@@ -29,6 +29,7 @@
%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%% POSSIBILITY OF SUCH DAMAGE.
-module(ewpcap).
+-include("ewpcap.hrl").
-export([
open/0, open/1, open/2,
@@ -92,32 +93,34 @@ open(Dev, Options) when is_list(Options) ->
pcap_open_live(Dev, Snaplen, Promisc, To_ms).
-close(Res) ->
+close(#ewpcap_resource{res = Res}) ->
pcap_close(Res).
filter(Res, Filter) ->
filter(Res, Filter, []).
-filter(Res, Filter, Options) when is_binary(Filter); is_list(Filter) ->
+filter(#ewpcap_resource{res = Res}, Filter, Options) when is_binary(Filter); is_list(Filter) ->
Optimize = bool(proplists:get_value(optimize, Options, true)),
Netmask = mask(proplists:get_value(netmask, Options, ?PCAP_NETMASK_UNKNOWN)),
pcap_compile(Res, Filter, Optimize, Netmask).
-loop(Res) ->
+loop(#ewpcap_resource{res = Res}) ->
pcap_loop(Res).
read(Res) ->
read(Res, infinity).
-read(_Res, Timeout) ->
+read(#ewpcap_resource{ref = Ref}, Timeout) ->
receive
- {packet, DatalinkType, Time, ActualLength, Packet} ->
- {ok, {packet, DatalinkType, Time, ActualLength, Packet}}
+ {ewpcap, Ref, _DatalinkType, _Time, _ActualLength, Packet} ->
+ {ok, Packet};
+ {ewpcap_error, Ref, Error} ->
+ {error, Error}
after
Timeout -> {error, eagain}
end.
-write(Res, Data) when is_list(Data); is_binary(Data) ->
+write(#ewpcap_resource{res = Res}, Data) when is_list(Data); is_binary(Data) ->
pcap_sendpacket(Res, Data).

0 comments on commit 44bfe79

Please sign in to comment.