`
xumingyong
  • 浏览: 182377 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

Use Erlang NIF to snoop, capture packets(in Windows XP), in OTP-R13B04

阅读更多

1. 介绍

 

在上一篇博文http://xumingyong.iteye.com/blog/586743中,我在Erlang/OTP-R13B03中,用nif实现了抓网络包功能,但是由于在R13B04版本中,NIF接口形式发生了变化(见下一段在线文档摘要),所以我的源码也做了相应修改。

---------------------------------------------------------------------------------

The NIF concept was introduced in R13B03 as an EXPERIMENTAL feature. The interfaces may be changed in any way in coming releases. The plan is however to lift the experimental label and maintain interface backward compatibility from R14B.

Incompatible changes in R13B04:

  • The function prototypes of the NIFs have changed to expect argc and argv arguments. The arity of a NIF is by that no longer limited to 3.
  • enif_get_data renamed as enif_priv_data.
  • enif_make_string got a third argument for character encoding.

----------------------------------------------------------------------------------

 

2. nif_R13B04.c

 

主要是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 xumingyong@gmail.com
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

 

主要变化是:在R13B03中on_load函数的返回值必须是true,在R13B04中不需要了。

 

%%% 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

 

另外:需要注意的是,R13B04的lib\tools-2.6.5.1\emacs目录下,缺少了erlang-skels.el和erlang-skels-old.el两个文件,导致EMACS不能正常进行语法高亮显示,可在源码中将这两个文件拷贝到该目录下即可。一个小小的bug。

 

 

 

 

 

分享到:
评论
3 楼 mryufeng 2010-03-05  
解释epmd等2进制协议是erlang的拿手呀
2 楼 xumingyong 2010-03-03  
GeoffCant在git clone git://git.erlang.geek.nz/bgrep.git中用erlang binary实现了IPV4的解析。

erlsnoop是用纯C实现的,用erl应该会更简单,只是要实现一个完整的EPMD协议还是挺麻烦的。
1 楼 mryufeng 2010-03-02  
最好是做个erlsnoop兼容的 参看 http://mryufeng.iteye.com/blog/167695

这样就太方便大家了...

相关推荐

    otp-OTP-20.0.tar.gz

    在给定的压缩包文件"otp-OTP-20.0.tar.gz"中,我们可以推测这包含了OTP的版本20.0,这是一个针对Erlang语言的重要版本更新。 Erlang是一种静态类型、函数式编程语言,特别适合构建高并发、低延迟和容错性强的系统,...

    otp-win64-25.0.1.exe ErLang 下载

    **OTP (Open Telecom Platform) for Windows 64位 25.0.1 版本:ErLang 下载** ErLang是一种高级并发编程语言,由瑞典电信设备制造商Ericsson开发,用于构建高度可靠和可扩展的分布式系统。OTP(Open Telecom ...

    erlang-otp-win64位 25.3版本

    在Windows操作系统上,Erlang OTP通常提供为预编译的64位版本,如"erlang-otp-win64位 25.3版本",这确保了它能在Windows 64位环境下高效运行。 Erlang OTP(Open Telephony Platform)是一个开放源码的软件框架,...

    rustler编写erlang nif

    【标题】:“rustler编写erlang nif” 在Erlang生态系统中,Native Implemented Functions (NIFs) 是一种机制,允许开发者用其他语言(如C、C++或Rust)编写性能关键部分的代码,然后在Erlang虚拟机(VM)中调用。...

    erlang nif test

    Erlang NIF(Native Implemented Functions)是Erlang虚拟机提供的一种机制,允许开发者用C语言或者其他低级语言编写性能关键部分的代码,并在Erlang系统中无缝调用。这种方式可以充分利用C语言的高效性,同时保持...

    otp-win64-25.1.2 rabbitmq-server-3.11.2

    标题中的"otp-win64-25.1.2"指的是Erlang OTP(Open Telephony Platform)的一个64位Windows版本,版本号为25.1.2。Erlang OTP是一个开源平台,主要用于构建高可用性、分布式和实时系统。它提供了强大的并发处理、...

    rabbit mq windows64rabbitmq-server-3.12.4.exe otp-win64-26.0.ex

    在本案例中,我们讨论的是RabbitMQ的Windows 64位版本,即rabbitmq-server-3.12.4.exe,以及配套的Erlang环境 otp-win64-26.0.exe。Erlang是一种面向并发的编程语言,是RabbitMQ的基础,因为RabbitMQ是用Erlang编写...

    Erlang-OTP-19.0.zip

    Erlang OTP(Open Telephony Platform)是一种由Ericsson公司开发的编程语言Erlang的扩展框架,主要用于构建高可用性、容错性和分布式系统。Erlang OTP 19.0是该平台的一个重要版本,它包含了Erlang R19.1,这是一个...

    er-lang-otp-win64-24.0.7z

    Win10安装erlang,高版本,官网下载速度比较慢,网上资料大多名副其实,挂羊头卖狗肉,花费很长时间从官网下载的最新版本otp_win64_24.0,拿出来与大家分享

    Windows Erlang语言安装包:otp-win64-25.2.1

    Windows上安装RabbitMQ服务,依赖的Erlang语言安装包:otp_win64_25.2.1.exe 重要:Erlang安装程序必须【以管理员身份运行】,否则RabbitMQ安装程序相关信息不会在注册表项中不存在。

    otp_src_21.3.zip

    标题中的"otp-src-21.3.zip"指的是Erlang编程语言的源代码压缩包,版本为21.3。OTP(Open Telephony Platform)是Erlang的核心部分,包含了许多用于构建可靠、分布式系统的基础库和工具。Erlang因其在处理并发和容错...

    Erlang-OTP-API 离线查询英文全手册

    此“Erlang-OTP-API 离线查询英文全手册”是Erlang OTP的官方文档,包含了所有API的详细信息,是学习和开发Erlang OTP应用的重要资源。手册内容广泛,包括了以下几个核心部分: 1. **模块和函数**:手册详细列出了...

    otp-win64_20.1 64位

    标题中的"otp-win64_20.1 64位"指的是Erlang的 OTP (Open Telecom Platform) 的一个特定版本,适用于64位Windows操作系统。OTP是Erlang编程语言的核心部分,提供了一个强大的并发框架、分布式计算支持以及故障恢复...

    Linux部署RabbitMQ所需的Erlang环境相关工具包 otp-src-23.2.7.5

    用于linux下安装RabbitMQ ,首先安装Erlang环境,并且要注意rabbitmq版本和Erlang需版本对应 我安装的RabbitMQ 3.10.25和Erlang 23.2; Erlang 25.1(下载 地址:https://www.erlang.org/patches/otp-23.2.7.5) ...

    otp-OTP-19.3.zip

    OTP的核心是Erlang编程语言,它提供了强大的并发处理能力和容错机制,使得在分布式系统中实现高可用性成为可能。 在OTP 19.3版本中,我们看到的是一个经过精心设计和优化的版本,它包含了Erlang VM(BEAM)的改进...

    otp-win64-25.3.exe

    标题中的"otp-win64-25.3.exe"是一个针对Windows 64位操作系统的 OTP(Open Telecom Platform)软件的可执行文件。OTP是由Ericsson开发的一个开源框架,主要用于构建高度可靠、可扩展和可维护的分布式系统,尤其是...

    otp-system-documentation

    This section covers the installation process for the Erlang/OTP binary release on UNIX or Windows environments. ##### UNIX Installation **Introduction** The OTP system is distributed as a single ...

    otp_src_R11B-5.tar.gz_OTP_erlang_otp-src-R11B_otp_s

    OTP(Open Telephony Platform)是爱立信公司开发的一个开源软件框架,主要为构建高度可靠、可扩展且具有容错能力的分布式系统提供基础。它以其内置的编程语言Erlang为核心,Erlang是一种静态类型、函数式编程语言,...

    emqx-4.4.3-otp24.2.1-windows-amd64.zip

    EMQX,全称为Erlang MQTT Broker,是一款基于Erlang OTP开发的开源MQTT消息代理,专门针对物联网(IoT)设计。这个“emqx-4.4.3-otp24.2.1-windows-amd64.zip”压缩包文件包含了EMQ X Broker的4.4.3版本,适配于运行...

    otp-win64-26.0

    otp_win64_26.0安装包

Global site tag (gtag.js) - Google Analytics