You don't have javascript enabled. Good luck! :(

This is a test

You’ll find this post in your _posts directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run jekyll serve, which launches a web server and auto-regenerates your site when a file is updated.

To add new posts, simply add a file in the _posts directory that follows the convention YYYY-MM-DD-name-of-post.ext and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.

Jekyll also offers powerful support for code snippets:

def print_hi(name)
  puts "Hi, #{name}"
end
print_hi('Tom')
#=> prints 'Hi, Tom' to STDOUT.

Check out the Jekyll docs for more info on how to get the most out of Jekyll. File all bugs/feature requests at Jekyll’s GitHub repo. If you have questions, you can ask them on Jekyll Talk.

  May 17, 2017     WenYuan     網路程式設計  UPDATE: Jun 30, 2018

[Linux C] 使用 RAW socket 實現簡易的 Ping 功能

不管是在 Linux 或 Window 上 , 你一定或多或少使用過 Ping 這個指令吧! 使用 Ping 指令可以讓你迅速知道目標主機現在是否存活 (當然有其他例外) 。而這篇文章就是要探討 , 該如何使用 RAW socket 來實現簡單的 Ping 功能


關於 ICMP

我們在這次的實作中就是要利用 ICMP (網際網路控制訊息協定) 來實現 Ping 功能。這個協定用於 TCP/IP 網路中傳送控制訊息 , 提供可能發生在通訊環境中的各種問題回饋 , 而透過這些回饋資訊 , 就可以針對問題做出診斷 , 再採取應對的措施

所以意思就是 , 我們只要對目標主機發送 ICMP 封包的 , 再針對其反饋的封包資訊來加以分析 , 就可以知道目標主機目前是否存活 , 如同下圖所示

此外 , 雖然 ICMP 和 IP 都同屬 OSI 第三層協定 , 但是 ICMP 本身不能自行傳送 , 而是需要倚賴 IP 的幫忙才能傳送 , 這也表示需要兩台 PC 同時支援 IP , 才能使用 ICMP 來進行錯誤偵測與診斷


ICMP 封包結構

ICMP Header 從第 160 位元開始 , 結構如下圖所示

其各欄位功能大致如下:

  • Type : ICMP 種類 , 可用來識別其狀況

  • Code : 進一步劃分 ICMP Type , 可用來識別其錯誤原因

  • Checksum : 用來檢查封包資訊有無錯誤

  • ID : 由發送者所定 , 而目標主機的 Echo Reply 必須與我們所設的 ID 相同 , 所以可作為識別之用

  • Sequence : 用來紀錄序號 , 而 Echo Reply 同樣必須和發送端相同 , 也是作為識別之用的

ID Sequence 合起來就可以用來識別特定配對的 Echo Request 和 Echo Reply

而 Type 和 Code 所對應的網路狀況我在這裡就不贅述了 , 想要明白各種狀況可以到網際網路控制訊息協定察看哦~


RAW socket 實現 Ping 功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>

#ifndef _DEBUG_COLOR_
#define _DEBUG_COLOR_
    #define KDRK "\x1B[0;30m"
    #define KGRY "\x1B[1;30m"
    #define KRED "\x1B[0;31m"
    #define KRED_L "\x1B[1;31m"
    #define KGRN "\x1B[0;32m"
    #define KGRN_L "\x1B[1;32m"
    #define KYEL "\x1B[0;33m"
    #define KYEL_L "\x1B[1;33m"
    #define KBLU "\x1B[0;34m"
    #define KBLU_L "\x1B[1;34m"
    #define KMAG "\x1B[0;35m"
    #define KMAG_L "\x1B[1;35m"
    #define KCYN "\x1B[0;36m"
    #define KCYN_L "\x1B[1;36m"
    #define WHITE "\x1B[0;37m"
    #define WHITE_L "\x1B[1;37m"
    #define RESET "\x1B[0m"
#endif

// 做 checksum 運算, 驗證資料有無毀損
unsigned short checksum(unsigned short *buf, int bufsz){
    unsigned long sum = 0xffff;

    while(bufsz > 1){
        sum += *buf;
        buf++;
        bufsz -= 2;
    }

    if(bufsz == 1)
        sum += *(unsigned char*)buf;

    sum = (sum & 0xffff) + (sum >> 16);
    sum = (sum & 0xffff) + (sum >> 16);

    return ~sum;
}

int main(int argc, char *argv[]){
    int sd;
    struct icmphdr hdr;
    struct sockaddr_in addr;
    int num;
    char buf[1024];
    struct icmphdr *icmphdrptr;
    struct iphdr *iphdrptr;

    if(argc != 2){
        printf("usage: %s IPADDR\n", argv[0]);
        exit(-1);
    }

    addr.sin_family = PF_INET; // IPv4

    // 將使用者輸入的 IP 轉成 network order
    num = inet_pton(PF_INET, argv[1], &addr.sin_addr);
    if(num < 0){
        perror("inet_pton");
        exit(-1);
    }

    // 開一個 IPv4 的 RAW Socket , 並且準備收取 ICMP 封包
    sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
    if(sd < 0){
        perror("socket");
        exit(-1);
    }

    // 清空結構內容
    memset(&hdr, 0, sizeof(hdr));

    // 初始化 ICMP Header
    hdr.type = ICMP_ECHO;
    hdr.code = 0;
    hdr.checksum = 0;
    hdr.un.echo.id = 0;
    hdr.un.echo.sequence = 0;

    // 計算出 checksum
    hdr.checksum = checksum((unsigned short*)&hdr, sizeof(hdr));

    // 將定義好的 ICMP Header 送到目標主機
    num = sendto(sd, (char*)&hdr, sizeof(hdr), 0, (struct sockaddr*)&addr, sizeof(addr));
    if(num < 1){
        perror("sendto");
        exit(-1);
    }
    printf(KYEL"We have sended an ICMP packet to %s\n", argv[1]);

    // 清空 buf
    memset(buf, 0, sizeof(buf));

    printf(KGRN"Waiting for ICMP echo...\n");

    // 接收來自目標主機的 Echo Reply
    num = recv(sd, buf, sizeof(buf), 0);
    if(num < 1){
        perror("recv");
        exit(-1);
    }

    // 取出 IP Header
    iphdrptr = (struct iphdr*)buf;

    // 取出 ICMP Header
    icmphdrptr = (struct icmphdr*)(buf+(iphdrptr->ihl)*4);

    // 判斷 ICMP 種類
    switch(icmphdrptr->type){
        case 3:
            printf(KBLU"The host %s is a unreachable purpose!\n", argv[1]);
            printf(KBLU"The ICMP type is %d\n", icmphdrptr->type);
            printf(KBLU"The ICMP code is %d\n", icmphdrptr->code);
            break;
        case 8:
            printf(KRED"The host %s is alive!\n", argv[1]);
            printf(KRED"The ICMP type is %d\n", icmphdrptr->type);
            printf(KRED"The ICMP code is %d\n", icmphdrptr->code);
            break;
        case 0:
            printf(KRED"The host %s is alive!\n", argv[1]);
            printf(KRED"The ICMP type is %d\n", icmphdrptr->type);
            printf(KRED"The ICMP code is %d\n", icmphdrptr->code);
            break;
        default:
            printf(KMAG"Another situations!\n");
            printf(KMAG"The ICMP type is %d\n", icmphdrptr->type);
            printf(KMAG"The ICMP code is %d\n", icmphdrptr->code);
            break;
    }

    close(sd); // 關閉 socket
    return EXIT_SUCCESS;
}
  • Ping 中華電信 IP 試試看

  • Ping 不存在的 IP 試試看


參考文獻