ping是使用ICMP協(xié)議
ICMP協(xié)議的組成:Type(8bits) + Code(8bits) + 校驗碼(checksum,8bits) + ID(16bits) + 序號(sequence脖阵,16bits) + 數(shù)據(jù)
這些組成部分的含義:
1)Type ICMP的類型,標識生成的錯誤報文
2)Code 進一步劃分ICMP的類型荷辕,該字段用來查找產(chǎn)生的原因;例如件豌,ICMP的目標不可達類型可以把這個位設為1至15等來表示不同的意思疮方。
3)CheckSum 校驗碼部分,這個字段包含從ICMP報頭和數(shù)據(jù)部分計算得來的茧彤,用于檢查錯誤的骡显,其中此校驗碼字段的值視為0.
4)ID 這個字段包含了ID值,在Echo Reply類型的消息中要返回這個字段曾掂。
5)Sequence 這個字段包含一個序號
ping命令的實現(xiàn)是使用ICMP中類型值為8(reply)和0(request)
現(xiàn)在開始編寫代碼:
一惫谤、解析參數(shù)
var (
icmp ICMP
laddr = net.IPAddr{IP: net.ParseIP("ip")}
//raddr, _ = net.ResolveIPAddr("ip", os.Args[1])
num int
timeout int64
size int
stop bool
)
func ParseArgs() {
flag.Int64Var(&timeout, "w", 1000, "等待每次回復的超時時間(毫秒)")
flag.IntVar(&num, "n", 4, "要發(fā)送的請求數(shù)")
flag.IntVar(&size, "l", 32, "要發(fā)送緩沖區(qū)大小")
flag.BoolVar(&stop, "t", false, "Ping 指定的主機,直到停止")
flag.Parse()
}
二珠洗、定義ICMP結(jié)構(gòu)體
type ICMP struct {
Type uint8
Code uint8
Checksum uint16
Identifier uint16
SequenceNum uint16
}
三溜歪、為ICMP變量設置值
//icmp頭部填充
icmp.Type = 8
icmp.Code = 0
icmp.Checksum = 0
icmp.Identifier = 1
icmp.SequenceNum = 1
四、計算ICMP校驗和
這邊講解下校驗和的計算险污,ICMP的校驗和IP的校驗不同痹愚,ICMP的校驗是校驗ICMP頭部和數(shù)據(jù)內(nèi)容富岳,ICMP校驗和計算過程如下:
1)將ICMP頭部內(nèi)容中的校驗內(nèi)容(Checksum)的值設為0
2)將拼接好(Type+Code+Checksum+Id+Seq+傳輸Data)的ICMP包按Type開始每兩個字節(jié)一組(其中Checksum的兩個字節(jié)都看成0)蛔糯,進行加和處理,如果字節(jié)個數(shù)為奇數(shù)個窖式,則直接加上這個字節(jié)內(nèi)容蚁飒。說明:這個加和過程的內(nèi)容放在一個4字節(jié)上,如果溢出4字節(jié)萝喘,則將溢出的直接拋棄
3)將高16位與低16位內(nèi)容加和淮逻,直到高16為0
4)將步驟三得出的結(jié)果取反,得到的結(jié)果就是ICMP校驗和的值
驗證校驗和的方式也是一樣阁簸,驗證時先計算驗證和爬早,然后和驗證和中內(nèi)容進行比較是否一樣
func CheckSum(data []byte) uint16 {
var sum uint32
var length = len(data)
var index int
for length > 1 { // 溢出部分直接去除
sum += uint32(data[index])<<8 + uint32(data[index+1])
index += 2
length -= 2
}
if length == 1 {
sum += uint32(data[index])
}
sum = uint16(sum >> 16) + uint16(sum)
sum = uint16(sum >> 16) + uint16(sum)
return uint16(^sum)
}
五、發(fā)送ICMP包
六启妹、打印結(jié)果
完整實現(xiàn)代碼:
github下載鏈接:https://github.com/laijinhang/ping
package main
import (
"bytes"
"encoding/binary"
"flag"
"fmt"
"log"
"net"
"os"
"time"
"math"
)
type ICMP struct {
Type uint8
Code uint8
Checksum uint16
Identifier uint16
SequenceNum uint16
}
var (
icmp ICMP
laddr = net.IPAddr{IP: net.ParseIP("ip")}
num int
timeout int64
size int
stop bool
)
func main() {
ParseArgs()
args := os.Args
if len(args) < 2 {
Usage()
}
desIp := args[len(args) - 1]
conn, err := net.DialTimeout("ip:icmp", desIp, time.Duration(timeout) * time.Millisecond)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
//icmp頭部填充
icmp.Type = 8
icmp.Code = 0
icmp.Checksum = 0
icmp.Identifier = 1
icmp.SequenceNum = 1
fmt.Printf("\n正在 ping %s 具有 %d 字節(jié)的數(shù)據(jù):\n", desIp, size)
var buffer bytes.Buffer
binary.Write(&buffer, binary.BigEndian, icmp) // 以大端模式寫入
data := make([]byte, size) //
buffer.Write(data)
data = buffer.Bytes()
var SuccessTimes int // 成功次數(shù)
var FailTimes int // 失敗次數(shù)
var minTime int = int(math.MaxInt32)
var maxTime int
var totalTime int
for i := 0;i < num;i++ {
icmp.SequenceNum = uint16(1)
// 檢驗和設為0
data[2] = byte(0)
data[3] = byte(0)
data[6] = byte(icmp.SequenceNum >> 8)
data[7] = byte(icmp.SequenceNum)
icmp.Checksum = CheckSum(data)
data[2] = byte(icmp.Checksum >> 8)
data[3] = byte(icmp.Checksum)
// 開始時間
t1 := time.Now()
conn.SetDeadline(t1.Add(time.Duration(time.Duration(timeout) * time.Millisecond)))
n, err := conn.Write(data)
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 65535)
n, err = conn.Read(buf)
if err != nil {
fmt.Println("請求超時筛严。")
FailTimes++
continue
}
et := int(time.Since(t1) / 1000000)
if minTime > et {
minTime = et
}
if maxTime <et {
maxTime = et
}
totalTime += et
fmt.Printf("來自 %s 的回復: 字節(jié)=%d 時間=%dms TTL=%d\n", desIp, len(buf[28:n]), et, buf[8])
SuccessTimes++
time.Sleep(1 * time.Second)
}
fmt.Printf("\n%s 的 Ping 統(tǒng)計信息:\n", desIp)
fmt.Printf(" 數(shù)據(jù)包: 已發(fā)送 = %d,已接收 = %d饶米,丟失 = %d (%.2f%% 丟失)桨啃,\n", SuccessTimes + FailTimes, SuccessTimes, FailTimes, float64(FailTimes * 100) / float64(SuccessTimes + FailTimes))
if maxTime != 0 && minTime != int(math.MaxInt32) {
fmt.Printf("往返行程的估計時間(以毫秒為單位):\n")
fmt.Printf(" 最短 = %dms车胡,最長 = %dms,平均 = %dms\n", minTime, maxTime, totalTime / SuccessTimes)
}
}
func CheckSum(data []byte) uint16 {
var sum uint32
var length = len(data)
var index int
for length > 1 { // 溢出部分直接去除
sum += uint32(data[index]) << 8 + uint32(data[index+1])
index += 2
length -= 2
}
if length == 1 {
sum += uint32(data[index])
}
// CheckSum的值是16位照瘾,計算是將高16位加低16位匈棘,得到的結(jié)果進行重復以該方式進行計算,直到高16位為0
/*
sum的最大情況是:ffffffff
第一次高16位+低16位:ffff + ffff = 1fffe
第二次高16位+低16位:0001 + fffe = ffff
即推出一個結(jié)論析命,只要第一次高16位+低16位的結(jié)果主卫,再進行之前的計算結(jié)果用到高16位+低16位,即可處理溢出情況
*/
sum = uint32(sum >> 16) + uint32(sum)
sum = uint32(sum >> 16) + uint32(sum)
return uint16(^sum)
}
func ParseArgs() {
flag.Int64Var(&timeout, "w", 1500, "等待每次回復的超時時間(毫秒)")
flag.IntVar(&num, "n", 4, "要發(fā)送的請求數(shù)")
flag.IntVar(&size, "l", 32, "要發(fā)送緩沖區(qū)大小")
flag.BoolVar(&stop, "t", false, "Ping 指定的主機鹃愤,直到停止")
flag.Parse()
}
func Usage() {
argNum := len(os.Args)
if argNum < 2 {
fmt.Print(
`
用法: ping [-t] [-a] [-n count] [-l size] [-f] [-i TTL] [-v TOS]
[-r count] [-s count] [[-j host-list] | [-k host-list]]
[-w timeout] [-R] [-S srcaddr] [-c compartment] [-p]
[-4] [-6] target_name
選項:
-t Ping 指定的主機队秩,直到停止。
若要查看統(tǒng)計信息并繼續(xù)操作昼浦,請鍵入 Ctrl+Break馍资;
若要停止,請鍵入 Ctrl+C关噪。
-a 將地址解析為主機名鸟蟹。
-n count 要發(fā)送的回顯請求數(shù)。
-l size 發(fā)送緩沖區(qū)大小使兔。
-f 在數(shù)據(jù)包中設置“不分段”標記(僅適用于 IPv4)建钥。
-i TTL 生存時間。
-v TOS 服務類型(僅適用于 IPv4虐沥。該設置已被棄用熊经,
對 IP 標頭中的服務類型字段沒有任何
影響)。
-r count 記錄計數(shù)躍點的路由(僅適用于 IPv4)欲险。
-s count 計數(shù)躍點的時間戳(僅適用于 IPv4)镐依。
-j host-list 與主機列表一起使用的松散源路由(僅適用于 IPv4)。
-k host-list 與主機列表一起使用的嚴格源路由(僅適用于 IPv4)天试。
-w timeout 等待每次回復的超時時間(毫秒)槐壳。
-R 同樣使用路由標頭測試反向路由(僅適用于 IPv6)。
根據(jù) RFC 5095喜每,已棄用此路由標頭务唐。
如果使用此標頭,某些系統(tǒng)可能丟棄
回顯請求带兜。
-S srcaddr 要使用的源地址枫笛。
-c compartment 路由隔離艙標識符。
-p Ping Hyper-V 網(wǎng)絡虛擬化提供程序地址刚照。
-4 強制使用 IPv4刑巧。
-6 強制使用 IPv6。
`)
}
}
參考文章:
1)https://blog.csdn.net/zhj082/article/details/80518322
2)https://blog.csdn.net/simplelovecs/article/details/51146960
3)https://blog.csdn.net/gophers/article/details/21481447
4)https://blog.csdn.net/zhj082/article/details/80518322