Use Errlang NIF to snoop,capture packets(in Windows XP),in OTP-13B 04
1.紹介
前のブログhttp://xumingyong.iteye.com/blog/586743では、Errlang/OTP-133 B 03において、nifを使ってネットワークパケットをつかむ機能を実現しましたが、R 13 B 04バージョンにおいて、NIFインターフェースの形式が変化しています.
---------------------------------------
The NIF concept was introduced in R 13 B 03 as an EXPERIMENTAL feature.The interfaces may be changed in any way in comming releass.The pln is however to lift the experimental label and mantain interface backward partity
Incomptible changes in R 13 B 04: The function prototypes of the NIFs have changed to expect argc and argv argv argments.The Rity of a NIF is by thatのlonger limited to 3. enif_ゲットするdata renamed as enif_prvdata. enif_メークstring got a third argment for character encoding. --------------------------------------------
2.nif_R 13 B 04.
主にnif関数の宣言形式が変化した.
3.nif.h
4.Makefile
5.nif.erl
主な変化はR 13 B 03におけるon_である.ロード関数の戻り値はtrueでなければなりません.R 13 B 04には不要です.
6.テスト
省略します.前のブログhttp://xumingyong.iteye.com/blog/586743を見てください.
また、R 13 B 04のlib\tools-2.6.5.1\emacsディレクトリの下で、erlang-skers.elとerlang-skels-old.elの二つのファイルが不足しています.EMACSは正常に文法ハイライト表示ができなくなり、ソースからこの二つのファイルをこのディレクトリにコピーすればいいです.小さなバグ.
前のブログhttp://xumingyong.iteye.com/blog/586743では、Errlang/OTP-133 B 03において、nifを使ってネットワークパケットをつかむ機能を実現しましたが、R 13 B 04バージョンにおいて、NIFインターフェースの形式が変化しています.
---------------------------------------
The NIF concept was introduced in R 13 B 03 as an EXPERIMENTAL feature.The interfaces may be changed in any way in comming releass.The pln is however to lift the experimental label and mantain interface backward partity
Incomptible changes in R 13 B 04:
2.nif_R 13 B 04.
主にnif関数の宣言形式が変化した.
/* This file used to create a Erlang NIF which sniffer network packets. */
#include "nif.h"
pcap_t *devHandler = NULL;
static ERL_NIF_TERM lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int i = 0;
char errbuf[PCAP_ERRBUF_SIZE], str[1024], str1[1024], num[6];
pcap_if_t *alldevs, *d;
memset(errbuf, 0, PCAP_ERRBUF_SIZE);
memset(str, 0, 1024);
memset(str1, 0, 1024);
if (pcap_findalldevs_ex("rpcap://", NULL /* auth is not needed */, &alldevs, errbuf) == -1)
return enif_make_string(env, errbuf, ERL_NIF_LATIN1);
for(d= alldevs; d != NULL; d= d->next)
{
strcat(str, "{");
itoa(++i, num, 10);
strcat(str, num);
strcat(str, ", ");
strcat(str, d->name);
strcat(str, ", ");
if (d->description)
strcat(str, d->description);
else
strcat(str,"null");
strcat(str, "}, ");
}
pcap_freealldevs(alldevs);
return enif_make_string(env, str, ERL_NIF_LATIN1);
}
static ERL_NIF_TERM opendevice(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
/* char dev[64]; */
char dev[1024];
int ip, res, i, j;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *alldevs, *d;
memset(errbuf, 0, PCAP_ERRBUF_SIZE);
memset(dev, 0, 1024);
/* argv[0] = interface index */
res = enif_get_int(env, argv[0], &ip);
if(res < 0)
{
return enif_make_string(env, "error argument", ERL_NIF_LATIN1);
}
if (pcap_findalldevs_ex("rpcap://", NULL /* auth is not needed */, &alldevs, errbuf) == -1)
return enif_make_string(env, errbuf, ERL_NIF_LATIN1);
for(d = alldevs, i = 1; d != NULL; d = d->next, i++)
{
if(i == ip)
strcpy(dev, d->name);
}
pcap_freealldevs(alldevs);
/* Parms: dev,snaplen,promisc,timeout_ms,errbuf
* to_ms=0 means wait enough packet to arrive.
*/
devHandler = pcap_open_live(dev, 65535, 1, 0, errbuf);
if(devHandler != NULL)
return enif_make_atom(env, "ok");
else
return enif_make_string(env, errbuf, ERL_NIF_LATIN1);
}
static ERL_NIF_TERM capture(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
int i, res;
struct pcap_pkthdr *pkthdr;
const u_char *packet = NULL;
ErlNifBinary bin;
res = pcap_next_ex(devHandler, &pkthdr, &packet);
if(res > 0)
{
enif_alloc_binary(env, pkthdr->len, &bin);
for(i = 0; i < pkthdr->len; i++)
{
bin.data[i] = packet[i];
}
bin.size = pkthdr->len;
return enif_make_binary(env, &bin);
}
else if(res == 0)
{
return enif_make_string(env, "Timeout", ERL_NIF_LATIN1);
}
else if(res == -1)
{
return enif_make_string(env, pcap_geterr(devHandler), ERL_NIF_LATIN1);
}
else
{
return enif_make_string(env, "Unknown error", ERL_NIF_LATIN1);
}
}
static ErlNifFunc funcs[] =
{
{"lookup", 0, lookup},
{"capture", 0, capture},
{"opendevice", 1, opendevice}
};
ERL_NIF_INIT(nif,funcs,NULL,NULL,NULL,NULL)
3.nif.h
#include "erl_nif.h"
#include "stdio.h"
#include "pcap.h"
#include "string.h"
#include "ctype.h"
#include "stdlib.h"
#ifndef NIF_H
#define NIF_H
#ifdef __cplusplus
extern "C" {
#endif
static ERL_NIF_TERM opendevice(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM capture(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM lookup(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
#ifdef __cplusplus
}
#endif
#endif
4.Makefile
# by [email protected]
all: nif_dll nif.beam
# for win32 dll compiler
# CFLAGS -g used for to generate debug info, used by gdb command.
CC = gcc
CFLAGS = -shared
INPUT = nif_R13B04.c wpcap.lib
nif_dll: nif.h
$(CC) $(CFLAGS) -o nif.dll $(INPUT)
# for erlang beam compiler
ERL = erlc
.SUFFIXES: .erl .beam
.erl.beam:
$(ERL) $<
clean:
del *.beam *.dll erl_crash.dump
5.nif.erl
主な変化はR 13 B 03におけるon_である.ロード関数の戻り値はtrueでなければなりません.R 13 B 04には不要です.
%%% nif sniffer
-module(nif).
-on_load(on_load/0).
-compile(export_all).
%%============================================================
%% Automatically loaded when load the module.
on_load() ->
ok = erlang:load_nif("./nif", 0).
start(InterfaceIndex, CaptureNum) ->
case opendevice(InterfaceIndex) of
ok ->
loop(CaptureNum);
_Any ->
{error, opendevice_fail}
end.
%% return a tuple, with{id, name, description}...
lookup() ->
error.
%check the lookup() return, here use the Interface id, e.g. 1, 2 or ...
opendevice(_Interface) ->
error.
capture() ->
error.
loop(0) ->
io:format("Time:~w ", [time()]);
loop(Count) ->
Pkt = capture(),
parser(Pkt),
loop(Count-1).
%%============================================================
parser(Pkt) when is_binary(Pkt) ->
<<_Addr:12/binary-little,
TypeOrLen:2/binary-little,
_Res/binary-little>> = Pkt,
case (TypeOrLen > <<16#05, 16#DC>>) of
true ->
parser(ethernet, Pkt);
false ->
parser(ieee, Pkt)
end;
parser(Pkt) when not(is_binary(Pkt)) ->
io:format("---Not a binary data-------------------------------~n"),
io:format("\t~p~n", [Pkt]).
parser(ethernet, Pkt) ->
<<Dst:6/binary-little,
Src:6/binary-little,
Type:2/binary-little,
Res/binary-little>> = Pkt,
io:format("---Ethernet II frame-------------------------------"),
io:format("~nDst MAC:\t"), printHex(Dst),
io:format("~nSrc MAC:\t"), printHex(Src),
io:format("~nType:\t\t"), printHex(Type), printEthernetIIType(Type),
io:format("~nData:\t\t"), printHex(Res),
io:format("~n~n");
parser(ieee, Pkt) ->
<<Dst:6/binary-little,
Src:6/binary-little,
Len:2/binary-little,
DSAP:1/binary-little,
SSAP:1/binary-little,
Ctrl:1/binary-little, % ieee802.2 class 1, connectionless type. Normally be 0x03.
Res/binary-little>> = Pkt,
io:format("---IEEE802.3 frame-------------------------------"),
io:format("~nDst MAC:\t"), printHex(Dst),
io:format("~nSrc MAC:\t"), printHex(Src),
io:format("~nTotal Length:\t"), printHex(Len),
io:format("~nDSAP:\t\t"), printHex(DSAP), printIeeeDSAP(DSAP),
io:format("~nSSAP:\t\t"), printHex(SSAP), printIeeeSSAP(SSAP),
io:format("~nControl:\t"), printHex(Ctrl),
io:format("~nData:\t\t"), printHex(Res),
io:format("~n~n").
printHex(Bin) ->
[io:format("~2.16.0B ",[X]) || X <- binary_to_list(Bin)].
printEthernetIIType(Type) ->
case Type of
<<16#08, 16#06>> ->
io:format("\t= ARP");
<<16#08, 16#00>> ->
io:format("\t= IP");
<<16#02, 16#00>> ->
io:format("\t= PUP");
<<16#80, 16#35>> ->
io:format("\t= RARP");
<<16#86, 16#DD>> ->
io:format("\t= IPv6");
<<16#88, 16#63>> ->
io:format("\t= PPPOE Discovery");
<<16#88, 16#64>> ->
io:format("\t= PPPoE Session");
<<16#88, 16#47>> ->
io:format("\t= MPLS Unicast");
<<16#88, 16#48>> ->
io:format("\t= MPLS Multicast");
<<16#81, 16#37>> ->
io:format("\t= IPX/SPX");
<<16#80, 16#00>> ->
io:format("\t= IS-IS");
<<16#88, 16#09>> ->
io:format("\t= LACP");
<<16#88, 16#8E>> ->
io:format("\t= 802.1x");
<<16#81, 16#4C>> ->
io:format("\t= SNMP");
<<16#88, 16#0B>> ->
io:format("\t= PPP");
<<16#88, 16#A7>> ->
io:format("\t= Cluster");
<<16#90, 16#00>> ->
io:format("\t= Loopback");
<<16#90, 16#10>> ->
io:format("\t= Vlan Tag");
<<16#90, 16#20>> ->
io:format("\t= Vlan Tag");
_Any ->
io:format("\t= unknown")
end.
printIeeeDSAP(Dsap) ->
<<IG>> = Dsap,
case IG bsr 7 of
0 ->
io:format("\t I/G=0 (Individual address)");
1 ->
io:format("\t I/G=1 (Group address)")
end.
printIeeeSSAP(Ssap) ->
<<CR>> = Ssap,
case CR bsr 7 of
0 ->
io:format("\t C/R=0 (Command)");
1 ->
io:format("\t C/R=1 (Response)")
end.
6.テスト
省略します.前のブログhttp://xumingyong.iteye.com/blog/586743を見てください.
また、R 13 B 04のlib\tools-2.6.5.1\emacsディレクトリの下で、erlang-skers.elとerlang-skels-old.elの二つのファイルが不足しています.EMACSは正常に文法ハイライト表示ができなくなり、ソースからこの二つのファイルをこのディレクトリにコピーすればいいです.小さなバグ.