Heim >Backend-Entwicklung >Golang >Wie stelle ich die Korrektheit der für die native „Ping'-Implementierung erstellten IPv4-Pakete sicher?
php-Editor Strawberry zeigt Ihnen, wie Sie die Korrektheit der IPv4-Pakete sicherstellen, die für die lokale „Ping“-Implementierung erstellt wurden. Verwenden Sie bei der Netzwerkkommunikation den Ping-Befehl, um die Konnektivität zwischen Hosts zu testen. In praktischen Anwendungen müssen wir jedoch die Korrektheit der gesendeten IPv4-Datenpakete sicherstellen, um Fehler oder Verluste zu vermeiden. Zu diesem Zweck können wir einige Maßnahmen ergreifen, um die Genauigkeit und Vollständigkeit der Datenpakete sicherzustellen, um sicherzustellen, dass wir genaue Ping-Ergebnisse erhalten. Schauen wir uns als Nächstes diese Maßnahmen an.
Ich habe an einem Nebenprojekt gearbeitet, bei dem es sich im Wesentlichen um ein Tool zur Fehlerbehebung im Netzwerk handelt. Mein Ziel ist es, mein Verständnis der Netzwerkgrundlagen zu vertiefen und die vom Betriebssystem bereitgestellten Fehlerbehebungstools besser zu nutzen.
Dies ist eine CLI-Anwendung, die den Hostnamen abruft und versucht, das Problem zu diagnostizieren, falls vorhanden. Der Plan besteht darin, zuerst Ping und Traceroute zu implementieren und dann nach und nach weitere Tools zu implementieren, je nach meinem Komfortniveau.
Allerdings ist meine Ping-Implementierung nicht korrekt, da die IPv4-Pakete fehlerhaft sind. Das sagt wireshark.
1 0.000000 192.168.0.100 142.250.195.132 ICMP 300 Unknown ICMP (obsolete or malformed?)
So habe ich es geschafft ping
<code>package ping import ( "encoding/json" "net" "github.com/pkg/errors" ) var ( IcmpProtocolNumber uint8 = 1 IPv4Version uint8 = 4 IPv4IHL uint8 = 5 ICMPHeaderType uint8 = 8 ICMPHeaderSubtype uint8 = 0 ) type NativePinger struct { SourceIP string DestIP string } type ICMPHeader struct { Type uint8 Code uint8 Checksum uint16 } type ICMPPacket struct { Header ICMPHeader Payload interface{} } type IPv4Header struct { SourceIP string DestinationIP string Length uint16 Identification uint16 FlagsAndOffset uint16 Checksum uint16 VersionIHL uint8 DSCPAndECN uint8 TTL uint8 Protocol uint8 } type IPv4Packet struct { Header IPv4Header Payload *ICMPPacket } func (p *NativePinger) createIPv4Packet() (*IPv4Packet, error) { versionIHL := (IPv4Version << 4) | IPv4IHL icmpPacket := &ICMPPacket{ Header: ICMPHeader{ Type: ICMPHeaderType, Code: ICMPHeaderSubtype, }, } ipv4Packet := &IPv4Packet{ Header: IPv4Header{ VersionIHL: versionIHL, DSCPAndECN: 0, Identification: 0, FlagsAndOffset: 0, TTL: 64, Protocol: IcmpProtocolNumber, SourceIP: p.SourceIP, DestinationIP: p.DestIP, }, Payload: icmpPacket, } ipv4Packet.Header.Length = 40 bytes, err := json.Marshal(icmpPacket) if err != nil { return nil, errors.Wrapf(err, "error converting ICMP packet to bytes") } icmpPacket.Header.Checksum = calculateChecksum(bytes) bytes, err = json.Marshal(ipv4Packet) if err != nil { return nil, errors.Wrapf(err, "error converting IPv4 packet to bytes") } ipv4Packet.Header.Checksum = calculateChecksum(bytes) return ipv4Packet, nil } func calculateChecksum(data []byte) uint16 { sum := uint32(0) // creating 16 bit words for i := 0; i < len(data)-1; i++ { word := uint32(data[i])<<8 | uint32(data[i+1]) sum += word } if len(data)%2 == 1 { sum += uint32(data[len(data)-1]) } // adding carry bits with lower 16 bits for (sum >> 16) > 0 { sum = (sum & 0xffff) + (sum >> 16) } // taking one's compliment checksum := ^sum return uint16(checksum) } func (p *NativePinger) ResolveAddress(dest string) error { ips, err := net.LookupIP(dest) if err != nil { return errors.Wrapf(err, "error resolving address of remote host") } for _, ip := range ips { if ipv4 := ip.To4(); ipv4 != nil { p.DestIP = ipv4.String() } } // The destination address does not need to exist as unlike tcp, udp does not require a handshake. // The goal here is to retrieve the outbound IP. Source: https://stackoverflow.com/a/37382208/3728336 // conn, err := net.Dial("udp", "8.8.8.8:80") if err != nil { return errors.Wrapf(err, "error resolving outbound ip address of local machine") } defer conn.Close() p.SourceIP = conn.LocalAddr().(*net.UDPAddr).IP.String() return nil } func (p *NativePinger) Ping(host string) error { if err := p.ResolveAddress(host); err != nil { return errors.Wrapf(err, "error resolving source/destination addresses") } packet, err := p.createIPv4Packet() if err != nil { return errors.Wrapf(err, "error creating IPv4Packet") } conn, err := net.Dial("ip4:icmp", packet.Header.DestinationIP) if err != nil { return errors.Wrapf(err, "error eshtablishing connection with %s", host) } defer conn.Close() bytes, err := json.Marshal(packet) if err != nil { return errors.Wrapf(err, "error converting IPv4 packet into bytes") } _, err = conn.Write(bytes) if err != nil { return errors.Wrapf(err, "error sending ICMP echo request") } buff := make([]byte, 2048) _, err = conn.Read(buff) // The implementation doesn't proceed beyond this point if err != nil { return errors.Wrapf(err, "error receiving ICMP echo response") } return nil } </code>
Ich bin mir nicht sicher, ob die Paketfehlbildung durch eine einzelne Ursache oder durch mehrere Ursachen verursacht wird. Ich denke, das Problem liegt an einer dieser beiden Stellen (oder an beiden?):
40 字节(wordsize = 4 字节)
berechnet. Schreiben Sie Strukturfelder in einer Reihenfolge, die eine Beschädigung der Struktur verhindert.
Ich verweise auf diese Quelle für Informationen zu verschiedenen Größentypen. <code>// 1 word (4 bytes) type ICMPHeader struct { Type uint8 // 8 bit Code uint8 // 8 bit Checksum uint16 // 16 bit } // 3 words (3*4 = 12 bytes) type ICMPPacket struct { Header ICMPHeader // 1 word Payload interface{} // 2 words } // 7 words (7*4 = 28 bytes) type IPv4Header struct { // Below group takes 4 words (each string takes 2 words) SourceIP string DestinationIP string // Following group takes 2 words (each 16 bits) Length uint16 Identification uint16 FlagsAndOffset uint16 Checksum uint16 // Below group takes 1 word (each takes 8 bits) VersionIHL uint8 DSCPAndECN uint8 TTL uint8 Protocol uint8 } // 10 words (40 bytes) type IPv4Packet struct { Header IPv4Header // 7 words as calculated above Payload ICMPPacket // 3 words as calculated above } </code>
In der Implementierung fehlen einige Teile, wie z. B. das Konfigurieren von Zählungen, das Zuweisen von Sequenznummern zu Paketen usw., aber vorher muss die grundlegende Implementierung korrigiert werden, d. h. der Empfang von Antworten für ICMP-ECHO-Pakete. Gut zu wissen, wo ich den Fehler gemacht habe.
Danke!
Unter Berücksichtigung der Ratschläge, die ich in den Kommentaren erhalten habe, habe ich den Code aktualisiert, um die Bytereihenfolge zu korrigieren und Rohbytes für die Quelladresse und die Zieladresse zu verwenden. Allerdings löst das allein das Problem nicht, das Paket ist immer noch fehlerhaft, also muss etwas anderes im Gange sein.
Endlich habe ich es zum Laufen gebracht. Ich sollte über ein paar Probleme mit dem Code sprechen.
Wie Andy richtig betont hat, sende ich ein JSON-Objekt, keine Rohbytes in Netzwerkbytereihenfolge. Dies wurde mit binary.Write(buf, binary.BigEndian, field)
Da diese Methode jedoch nur mit Werten fester Größe funktioniert, muss ich dies für jedes Strukturfeld tun, was den Code repetitiv und etwas hässlich macht.
Ich weiß, wie man den Wert des Feldes Version
和 IHL
字段组合在一起以优化内存的做法,这就是为什么我的结构中有这个单个字段 VersionIHL
。但是在序列化时,字段值(在本例中为 4 和 5)将被单独序列化,而我没有这样做。相反,我将整个 VersionIHL
in Bytes umwandelt.
Infolgedessen habe ich ein unerwartetes Oktett 69
,该字节流来自将 4
和 5
组合在一起的 0100 0101
im Byte-Stream gesendet.
Meine ICMP-Struktur enthält keine Kennungs- und Sequenznummernfelder. Die Informationen im ICMP-Datagramm-Header-Abschnitt auf Wikipedia wirken etwas allgemein gehalten. Allerdings fand ich die Details auf der RFC-Seite (Seite 14) viel aufschlussreicher.
Das fühlt sich seltsam an, wenn man bedenkt, wie wichtig die Seriennummer des Ping-Dienstprogramms ist. Während der Implementierung habe ich mich oft gefragt, wo die Seriennummer im Code richtig platziert ist. Erst als ich über die RFC-Seite gestolpert bin, hatte ich eine klare Vorstellung davon, wann und wo ich Seriennummern einbauen sollte.
Für alle, die Interesse haben könnten, hier ist der Funktionscode, den ich zusammengestellt habe.
Das obige ist der detaillierte Inhalt vonWie stelle ich die Korrektheit der für die native „Ping'-Implementierung erstellten IPv4-Pakete sicher?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!