靈修分享:亞略巴古的演講

亞略巴古的演說是使徒行傳一篇非常著名的講道。基督徒往往用這段經文來說明上帝的屬性,同時也會參考這段經文作為和外邦人互動的一個參考方式。最近因為團契查經查到這一段,重新思想後看到了以前沒有考慮過的面向,在這邊記錄一下。

徒17:16-34保羅在雅典等候他們的時候,看見滿城都是偶像,就心裏着急;於是在會堂裏與猶太人和虔敬的人,並每日在市上所遇見的人,辯論。還有伊壁鳩魯和斯多亞兩門的學士,與他爭論。有的說:「這胡言亂語的要說甚麼?」有的說:「他似乎是傳說外邦鬼神的。」這話是因保羅傳講耶穌與復活的道。他們就把他帶到亞略‧巴古,說:「你所講的這新道,我們也可以知道嗎?因為你有些奇怪的事傳到我們耳中,我們願意知道這些事是甚麼意思。」(雅典人和住在那裏的客人都不顧別的事,只將新聞說說聽聽。)
保羅站在亞略‧巴古當中,說:「眾位雅典人哪,我看你們凡事很敬畏鬼神。我遊行的時候,觀看你們所敬拜的,遇見一座壇,上面寫着『未識之神』。你們所不認識而敬拜的,我現在告訴你們。創造宇宙和其中萬物的神,既是天地的主,就不住人手所造的殿,也不用人手服事,好像缺少甚麼;自己倒將生命、氣息、萬物,賜給萬人。他從一本本:有古卷是血脈造出萬族的人,住在全地上,並且預先定準他們的年限和所住的疆界,要叫他們尋求神,或者可以揣摩而得,其實他離我們各人不遠;我們生活、動作、存留,都在乎他。就如你們作詩的,有人說:『我們也是他所生的。』我們既是神所生的,就不當以為神的神性像人用手藝、心思所雕刻的金、銀、石。世人蒙昧無知的時候,神並不監察,如今卻吩咐各處的人都要悔改。因為他已經定了日子,要藉着他所設立的人按公義審判天下,並且叫他從死裏復活,給萬人作可信的憑據。」
眾人聽見從死裏復活的話,就有譏誚他的;又有人說:「我們再聽你講這個吧!」於是保羅從他們當中出去了。 但有幾個人貼近他,信了主,其中有亞略‧巴古的官丟尼修,並一個婦人,名叫大馬哩,還有別人一同信從。

對基督徒來說,保羅這篇講道講的真好,不但講出了上帝的超越性(像是不住人手所造的殿、也不是金銀石的彫刻),也帶出了耶穌基督復活的大能。但最近我才注意到眾人的反應:「眾人聽見從死裏復活的話,就有譏誚他的;又有人說:「我們再聽你講這個吧!」」不曉得你看到這一段有什麼感想?可能我是玻璃心吧,如果我是保羅,我大概會非常非常的難過,看起來似乎沒有人理會這個福音、這篇講道。這篇被基督…

libpcap 使用範例

最近因為工作上的需求,所以透過 libpcap 寫了一個小程式。程式紀錄在下面。應該不難懂,所以就不多做說明。程式主要參考sniffex,不過主要針對  DHCP 的封包進行攔截以及顯示。


#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#define SIZE_ETHERNET (14)
#define SIZE_UDP (8)
#define ETHER_ADDR_LEN (6)

const char *DHCP_MSG_TYPE_STR[9] =
{
    "None",
    "DHCP Discover",
    "DHCP Offer",
    "DHCP Request",
    "DHCP Decline",
    "DHCP ACK",
    "DHCP NACK",
    "DHCP Release",
    "DHCP Inform"
};

struct sniff_ethernet
{
    uint8_t  ether_dhost[ETHER_ADDR_LEN];    /* destination host address */
    uint8_t  ether_shost[ETHER_ADDR_LEN];    /* source host address */
    uint16_t ether_type;                     /* IP? ARP? RARP? etc */
};

struct sniff_ip
{
    uint8_t  ip_vhl;                 /* version << 4 | header length >> 2 */
    uint8_t  ip_tos;                 /* type of service */
    uint16_t ip_len;                 /* total length */
    uint16_t ip_id;                  /* identification */
    uint16_t ip_off;                 /* fragment offset field */
    #define IP_RF 0x8000             /* reserved fragment flag */
    #define IP_DF 0x4000             /* dont fragment flag */
    #define IP_MF 0x2000             /* more fragments flag */
    #define IP_OFFMASK 0x1fff        /* mask for fragmenting bits */
    uint8_t  ip_ttl;                 /* time to live */
    uint8_t  ip_p;                   /* protocol */
    uint16_t ip_sum;                 /* checksum */
    struct  in_addr ip_src,ip_dst;   /* source and dest address */
};
#define IP_HL(ip)               ( ( ( ip ) -> ip_vhl ) & 0x0f )
#define IP_V(ip)                ( ( ( ip ) -> ip_vhl ) >> 4 )

struct sniff_udp
{
    uint16_t udp_sport;             /* source port */
    uint16_t udp_dport;             /* destination port */
    uint16_t udp_len;               /* source port */
    uint16_t udp_sum;               /* source port */
};

struct sniff_dhcp
{
    uint8_t     dhcp_op;
    uint8_t     dhcp_htype;     // 1: ethernet
    uint8_t     dhcp_hlen;      // 6: ethernet address length
    uint8_t     dhcp_hops;
    uint32_t    dhcp_xid;
    uint16_t    dhcp_secs;
    uint16_t    dhcp_flags;
    struct  in_addr dhcp_ciaddr;    // Client IP address
    struct  in_addr dhcp_yiaddr;    // Your IP address
    struct  in_addr dhcp_siaddr;    // Server IP address
    struct  in_addr dhcp_giaddr;    // GW IP address
    uint8_t     dhcp_chaddr[16];    // Client HW address
    uint8_t     dhcp_legacy[192];   // For legacy bootps compatibility
    uint32_t    dhcp_magic_cookie;  // Magic Cookie
};

void parse_dhcp_packet( u_char *args, const struct pcap_pkthdr *header, const u_char *packet )
{
    static int count = 1;

    const struct sniff_ethernet *eth_hdr;
    const struct sniff_ip       *ip_hdr;
    const struct sniff_udp      *udp_hdr;
    const struct sniff_dhcp     *dhcp_hdr;
    const char                  *payload;

    int size_ip = 0;
    int size_dhcp_options = 0;

    uint8_t     *dhcp_options = NULL;
    uint8_t     dhcp_option_code = 0;
    uint8_t     dhcp_option_len = 0;
    char        hostname[260];          // 256 + 4

    printf( "Packet number: %d\n", count );

    eth_hdr = ( struct sniff_ethernet * ) packet;
    ip_hdr  = ( struct sniff_ip * )( packet + SIZE_ETHERNET );

    size_ip = IP_HL( ip_hdr ) * 4;

    printf( "\tIP Header length: %d bytes\n", size_ip );
    printf( "\tIP Src: %s\n", inet_ntoa( ip_hdr -> ip_src ) );
    printf( "\tIP Dst: %s\n", inet_ntoa( ip_hdr -> ip_dst ) );

    switch( ip_hdr -> ip_p )
    {
        case IPPROTO_TCP:
            printf( "\tProtocol: TCP\n" );
            break;
        case IPPROTO_UDP:
            printf( "\tProtocol: UDP\n" );
            break;
        case IPPROTO_ICMP:
            printf( "\tProtocol: ICMP\n" );
            break;
        default:
            printf( "\tProtocol: Others\n" );
            break;
    }

    if( ip_hdr -> ip_p != IPPROTO_UDP )
    {
        printf( "\t==> Only handle DHCP\n" );
        return;
    }

    udp_hdr = ( struct sniff_udp * )( packet + SIZE_ETHERNET + size_ip );

    printf( "\tSrc port: %d\n", ntohs( udp_hdr -> udp_sport ) );
    printf( "\tDst port: %d\n", ntohs( udp_hdr -> udp_dport ) );
    printf( "\tlength: %d\n", ntohs( udp_hdr -> udp_len ) );

    dhcp_hdr = ( struct sniff_dhcp * )( packet + SIZE_ETHERNET + size_ip + SIZE_UDP );

    printf( "\tDHCP OP: %02X\n", dhcp_hdr -> dhcp_op );
    printf( "\tDHCP HTYPE: %02X\n", dhcp_hdr -> dhcp_htype );
    printf( "\tDHCP HLEN: %02X\n", dhcp_hdr -> dhcp_hlen );
    printf( "\tDHCP HOPS: %02X\n", dhcp_hdr -> dhcp_hops );
    printf( "\tDHCP XID: %08X\n", dhcp_hdr -> dhcp_xid );
    printf( "\tDHCP SECS: %04X\n", dhcp_hdr -> dhcp_secs );
    printf( "\tDHCP FLAGS: %04X\n", dhcp_hdr -> dhcp_flags );
    printf( "\tDHCP CIADDR: %s\n", inet_ntoa( dhcp_hdr -> dhcp_ciaddr ) );
    printf( "\tDHCP YIADDR: %s\n", inet_ntoa( dhcp_hdr -> dhcp_yiaddr ) );
    printf( "\tDHCP SIADDR: %s\n", inet_ntoa( dhcp_hdr -> dhcp_siaddr ) );
    printf( "\tDHCP GIADDR: %s\n", inet_ntoa( dhcp_hdr -> dhcp_giaddr ) );
    printf( "\tDHCP CHADDR: %02X:%02X:%02X:%02X:%02X:%02X\n",
            dhcp_hdr -> dhcp_chaddr[0], dhcp_hdr -> dhcp_chaddr[1], dhcp_hdr -> dhcp_chaddr[2],
            dhcp_hdr -> dhcp_chaddr[3], dhcp_hdr -> dhcp_chaddr[4], dhcp_hdr -> dhcp_chaddr[5] );
    printf( "\tDHCP Magic Cookie: %08X\n", dhcp_hdr -> dhcp_magic_cookie );

    size_dhcp_options = ntohs( udp_hdr -> udp_len ) - sizeof( struct sniff_dhcp );
    dhcp_options = ( uint8_t * )( packet + SIZE_ETHERNET + size_ip + SIZE_UDP + sizeof( struct sniff_dhcp ) );

    while( size_dhcp_options > 0 )
    {
        dhcp_option_code = dhcp_options[0];

        if( dhcp_option_code == 0 ) // Padding, no length
        {
            size_dhcp_options--;
            dhcp_options++;
            continue;
        }

        dhcp_option_len = dhcp_options[1];

        printf( "\t\tDHCP Option (%d)", dhcp_option_code );
        switch( dhcp_option_code )
        {
            case 53:        // DHCP Message Type
                printf( ": DHCP Message Type\n" );
                printf( "\t\t\tLength: %d\n", dhcp_option_len );
                printf( "\t\t\tType: %s\n", DHCP_MSG_TYPE_STR[ dhcp_options[2] ] );
                break;
            case 50:        // Requested IP address
                printf( ": Requested IP Address\n" );
                printf( "\t\t\tLength: %d\n", dhcp_option_len );
                printf( "\t\t\tIP: %s\n", inet_ntoa( *( struct  in_addr * )( dhcp_options + 2 ) ) );
                break;
            case 12:        // Host name
                printf( ": Host Name\n" );
                printf( "\t\t\tLength: %d\n", dhcp_option_len );
                memset( hostname, 0, 260 );
                memcpy( hostname, dhcp_options + 2, dhcp_option_len );
                printf( "\t\t\tHost Name: %s\n", hostname );
                break;
            case 255:       // End
                printf( "\n\t\t\tLength: 0\n" );
                dhcp_option_len = -1;
                break;
            default:
                printf( "\n\t\t\tLength: %d\n", dhcp_option_len );
                break;
        }

        dhcp_options = dhcp_options + dhcp_option_len + 2;
        size_dhcp_options = size_dhcp_options - ( dhcp_option_len + 2 );
    }

    count++;

    return;
}

int main( int argc, char **argv )
{
    char *dev = "eth0";
    char errbuf[PCAP_ERRBUF_SIZE];

    pcap_t  *handle = NULL;
    char filter_exp[] = "port bootps";
    struct bpf_program fp;
    bpf_u_int32 mask;
    bpf_u_int32 net;

    printf( "%s: this process is a DHCP sniffer example.\n", argv[0] );

    if( argc == 1 )
    {
        // eth0
    }
    else if( argc == 2 )
    {
        dev = argv[1];
    }
    else
    {
        printf( "Usage:\n" );
        printf( "%s [iface]\n", argv[0] );
        printf( "ps: If no interface is given, default interface is eth0.\n" );
    }

    if( pcap_lookupnet( dev, &net, &mask, errbuf ) == -1 )
    {
        printf( "Cannot get IP and netmask for device %s.\n", dev );
        net = 0;
        mask = 0;
    }

    // Open the capture device.
    // 1: promiscuous mode
    // -1: never timeout
    handle = pcap_open_live( dev, 1518, 1, 1000, errbuf );

    if( handle == NULL )
    {
        printf( "Cannot open device %s.\n", dev );
        return -1;
    }

    // Compile the filter
    if( pcap_compile( handle, &fp, filter_exp, 0, net ) == -1 )
    {
        printf( "Cannot parse the filter: \n%s --> %s\n", filter_exp, pcap_geterr( handle ) );
        return -1;
    }

    // Apply filter
    if( pcap_setfilter( handle, &fp ) == -1 )
    {
        printf( "Cannot install the filter: \n%s --> %s\n", filter_exp, pcap_geterr( handle ) );
        return -1;
    }

    // Start capture
    pcap_loop( handle, -1, parse_dhcp_packet, NULL );

    return 0;
}

不過討厭的地方在於,雖然 libpcap 有提供
pcap_get_selectable_fd ,但跟 select 或是 epoll 似乎不太容易結合使用,詳情請洽 manual。

留言

  1. 作者已經移除這則留言。

    回覆刪除
  2. 作者已經移除這則留言。

    回覆刪除
  3. Hi, 我是哲緯, 好久不見!

    剛好在查 libcap 看到你這篇文章,好奇問一下你的 BPF Filter 設定的 expression

    是不是只撈單一方向的 DHCP packets?

    因為 char filter_exp[] = "port bootps";

    我查了一下 "port bootps" 可以撈到 DHCPOFFER, DHCPACK 但是 DHCPDISCOVER, DHCPREQUEST 好像會被過濾掉,除非 Filter 下 "port bootps or port bootpc"

    thanks

    回覆刪除
    回覆
    1. Hello 好久不見,最近還好嗎?

      我剛剛實驗了一下,Request 和 ACK 都可以撈到。
      當初這樣寫的原因在於 DHCP 所用的 port 是 67(bootps) 和 68 (bootpc)
      我在 filter_exp 那邊並沒有指定 src port 或是 dst port
      所以只要有一端的 port 滿足應該就可以了

      刪除
    2. Blogger的留言好像系統怪怪的,我最近在教育部服替代役

      我想我大概瞭解了,因為 port bootps 的 filter expression 並沒有限制是source port或dest. port 所以兩個方向的封包,不管是以src=67 或是 dst=67都會撈到!

      不過如果filter expression 下 "bootps" 應該會比較好

      刪除

張貼留言

這個網誌中的熱門文章

如何將Linux打造成OpenFlow Switch:Openvswitch

我弟家的新居感恩禮拜分享:善頌善禱

如何利用 Wireshark 來監聽 IEEE 802.11 的管理封包