mirror of
https://github.com/impact-eintr/netstack.git
synced 2025-11-03 11:10:50 +08:00
ICMP实现 ip数据报分片机制还未实现
This commit is contained in:
BIN
cmd/arp/arp
BIN
cmd/arp/arp
Binary file not shown.
@@ -2,6 +2,7 @@ package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"netstack/tcpip"
|
||||
)
|
||||
|
||||
@@ -106,6 +107,12 @@ const (
|
||||
IPv4Any tcpip.Address = "\x00\x00\x00\x00"
|
||||
)
|
||||
|
||||
// Flags that may be set in an IPv4 packet.
|
||||
const (
|
||||
IPv4FlagMoreFragments = 1 << iota
|
||||
IPv4FlagDontFragment
|
||||
)
|
||||
|
||||
func IPVersion(b []byte) int {
|
||||
if len(b) < versIHL+1 {
|
||||
return -1
|
||||
@@ -216,6 +223,11 @@ func (b IPv4) SetDestinationAddress(addr tcpip.Address) {
|
||||
copy(b[dstAddr:dstAddr+IPv4AddressSize], addr)
|
||||
}
|
||||
|
||||
// CalculateChecksum calculates the checksum of the ipv4 header.
|
||||
func (b IPv4) CalculateChecksum() uint16 {
|
||||
return Checksum(b[:b.HeaderLength()], 0)
|
||||
}
|
||||
|
||||
// Encode encodes all the fields of the ipv4 header.
|
||||
func (b IPv4) Encode(i *IPv4Fields) {
|
||||
b[versIHL] = (4 << 4) | ((i.IHL / 4) & 0xf)
|
||||
@@ -264,3 +276,28 @@ func IsV4MulticastAddress(addr tcpip.Address) bool {
|
||||
}
|
||||
return (addr[0] & 0xf0) == 0xe0
|
||||
}
|
||||
|
||||
var ipv4Fmt string = `
|
||||
|% 4s|% 4s|% 8s| % 16s|
|
||||
| % 16s|%s|%s|%s|% 11s|
|
||||
| % 8s|% 8s|% 16s |
|
||||
|% 32s |
|
||||
|% 32s |
|
||||
| Options | Padding |
|
||||
%v
|
||||
`
|
||||
|
||||
type Types [] struct {}
|
||||
|
||||
func atoi[T int | int8 | int16 | int32 | int64 | uint | uint8 |uint16 | uint32](i T) string {
|
||||
return fmt.Sprintf("%d", i)
|
||||
}
|
||||
|
||||
func (b IPv4) String() string {
|
||||
return fmt.Sprintf(ipv4Fmt, atoi(IPVersion(b)), atoi(b.HeaderLength()), atoi(0), atoi(b.TotalLength()),
|
||||
atoi(b.ID()), atoi(b.Flags()>>2), atoi((b.Flags()&2)>>1), atoi(b.Flags()&1), atoi(b.FragmentOffset()),
|
||||
atoi(b.TTL()), atoi(b.Protocol()), atoi(b.Checksum()),
|
||||
b.SourceAddress().String(),
|
||||
b.DestinationAddress().String(),
|
||||
b.Payload())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fdbased
|
||||
|
||||
import (
|
||||
"log"
|
||||
"netstack/tcpip"
|
||||
"netstack/tcpip/buffer"
|
||||
"netstack/tcpip/header"
|
||||
@@ -126,6 +127,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable,
|
||||
ethHdr.SrcAddr = e.addr // 说明这是一个原始报文
|
||||
}
|
||||
eth.Encode(ethHdr) // 将以太帧信息作为报文头编入
|
||||
log.Println("链路层写回报文")
|
||||
// 写入网卡中
|
||||
if payload.Size() == 0 {
|
||||
return rawfile.NonBlockingWrite(e.fd, hdr.View())
|
||||
|
||||
128
tcpip/network/ipv4/icmp.go
Normal file
128
tcpip/network/ipv4/icmp.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"log"
|
||||
"netstack/tcpip"
|
||||
"netstack/tcpip/buffer"
|
||||
"netstack/tcpip/header"
|
||||
"netstack/tcpip/stack"
|
||||
)
|
||||
|
||||
/*
|
||||
ICMP 的全称是 Internet Control Message Protocol 。与 IP 协议一样同属 TCP/IP 模型中的网络层,并且 ICMP 数据包是包裹在 IP 数据包中的
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Code | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
| 不同的Type和Code有不同的内容 |
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
type echoRequest struct {
|
||||
r stack.Route
|
||||
v buffer.View
|
||||
}
|
||||
|
||||
// handleControl处理ICMP数据包包含导致ICMP发送的原始数据包的标头的情况。
|
||||
// 此信息用于确定必须通知哪个传输端点有关ICMP数据包。
|
||||
func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, vv buffer.VectorisedView) {
|
||||
h := header.IPv4(vv.First())
|
||||
|
||||
// We don't use IsValid() here because ICMP only requires that the IP
|
||||
// header plus 8 bytes of the transport header be included. So it's
|
||||
// likely that it is truncated, which would cause IsValid to return
|
||||
// false.
|
||||
//
|
||||
// Drop packet if it doesn't have the basic IPv4 header or if the
|
||||
// original source address doesn't match the endpoint's address.
|
||||
if len(h) < header.IPv4MinimumSize || h.SourceAddress() != e.id.LocalAddress {
|
||||
return
|
||||
}
|
||||
|
||||
hlen := int(h.HeaderLength())
|
||||
if vv.Size() < hlen || h.FragmentOffset() != 0 {
|
||||
// We won't be able to handle this if it doesn't contain the
|
||||
// full IPv4 header, or if it's a fragment not at offset 0
|
||||
// (because it won't have the transport header).
|
||||
return
|
||||
}
|
||||
|
||||
// Skip the ip header, then deliver control message.
|
||||
vv.TrimFront(hlen)
|
||||
p := h.TransportProtocol()
|
||||
e.dispatcher.DeliverTransportControlPacket(e.id.LocalAddress, h.DestinationAddress(), ProtocolNumber, p, typ, extra, vv)
|
||||
}
|
||||
|
||||
// 处理ICMP报文
|
||||
func (e *endpoint) handleICMP(r *stack.Route, vv buffer.VectorisedView) {
|
||||
v := vv.First()
|
||||
if len(v) < header.ICMPv4MinimumSize {
|
||||
return
|
||||
}
|
||||
h := header.ICMPv4(v)
|
||||
|
||||
// 更具icmp的类型来进行相应的处理
|
||||
switch h.Type() {
|
||||
case header.ICMPv4Echo: // icmp echo请求
|
||||
if len(v) < header.ICMPv4EchoMinimumSize {
|
||||
return
|
||||
}
|
||||
log.Printf("ICMP echo")
|
||||
vv.TrimFront(header.ICMPv4MinimumSize) // 去掉头部
|
||||
req := echoRequest{r: r.Clone(), v: vv.ToView()}
|
||||
select {
|
||||
case e.echoRequests <- req: // 发送给echoReplier处理 在那里会重新组一个头部
|
||||
default:
|
||||
req.r.Release()
|
||||
}
|
||||
|
||||
case header.ICMPv4EchoReply: // icmp echo响应
|
||||
if len(v) < header.ICMPv4EchoMinimumSize {
|
||||
return
|
||||
}
|
||||
e.dispatcher.DeliverTransportPacket(r, header.ICMPv4ProtocolNumber, vv)
|
||||
|
||||
case header.ICMPv4DstUnreachable: // 目标不可达
|
||||
if len(v) < header.ICMPv4DstUnreachableMinimumSize {
|
||||
return
|
||||
}
|
||||
vv.TrimFront(header.ICMPv4DstUnreachableMinimumSize)
|
||||
switch h.Code() {
|
||||
case header.ICMPv4PortUnreachable: // 端口不可达
|
||||
e.handleControl(stack.ControlPortUnreachable, 0, vv)
|
||||
|
||||
case header.ICMPv4FragmentationNeeded: // 需要进行分片但设置不分片标志
|
||||
mtu := uint32(binary.BigEndian.Uint16(v[header.ICMPv4DstUnreachableMinimumSize-2:]))
|
||||
e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理icmp echo请求的goroutine
|
||||
func (e *endpoint) echoReplier() {
|
||||
for req := range e.echoRequests {
|
||||
sendPing4(&req.r, 0, req.v)
|
||||
req.r.Release()
|
||||
}
|
||||
}
|
||||
|
||||
// 根据icmp echo请求,封装icmp echo响应报文,并传给ip层处理
|
||||
func sendPing4(r *stack.Route, code byte, data buffer.View) *tcpip.Error {
|
||||
hdr := buffer.NewPrependable(header.ICMPv4EchoMinimumSize + int(r.MaxHeaderLength()))
|
||||
|
||||
icmpv4 := header.ICMPv4(hdr.Prepend(header.ICMPv4EchoMinimumSize))
|
||||
icmpv4.SetType(header.ICMPv4EchoReply)
|
||||
icmpv4.SetCode(code)
|
||||
copy(icmpv4[header.ICMPv4MinimumSize:], data)
|
||||
data = data[header.ICMPv4EchoMinimumSize-header.ICMPv4MinimumSize:]
|
||||
icmpv4.SetChecksum(^header.Checksum(icmpv4, header.Checksum(data, 0)))
|
||||
|
||||
log.Printf("ICMP 回应报文组完 再次包装到IP报文")
|
||||
// 传给ip层处理
|
||||
return r.WritePacket(hdr, data.ToVectorisedView(), header.ICMPv4ProtocolNumber, r.DefaultTTL())
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"log"
|
||||
"netstack/tcpip"
|
||||
"netstack/tcpip/buffer"
|
||||
"netstack/tcpip/header"
|
||||
@@ -30,7 +31,12 @@ type endpoint struct {
|
||||
id stack.NetworkEndpointID
|
||||
// 链路端的表示
|
||||
linkEP stack.LinkEndpoint
|
||||
// TODO 需要添加
|
||||
// 报文分发器
|
||||
dispatcher stack.TransportDispatcher
|
||||
// ping请求报文接收队列
|
||||
echoRequests chan echoRequest
|
||||
// ip报文分片处理器
|
||||
//fragmentation *fragmentation.Fragmentation
|
||||
}
|
||||
|
||||
// DefaultTTL is the default time-to-live value for this endpoint.
|
||||
@@ -73,13 +79,82 @@ func (e *endpoint) MaxHeaderLength() uint16 {
|
||||
// 将传输层的数据封装加上IP头,并调用网卡的写入接口,写入IP报文
|
||||
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView,
|
||||
protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error {
|
||||
return nil
|
||||
// 预留ip报文的空间
|
||||
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
|
||||
length := uint16(hdr.UsedLength() + payload.Size())
|
||||
id := uint32(0)
|
||||
// 如果报文长度大于68
|
||||
if length > header.IPv4MaximumHeaderSize+8 {
|
||||
// Packets of 68 bytes or less are required by RFC 791 to not be
|
||||
// fragmented, so we only assign ids to larger packets.
|
||||
//id = atomic.AddUint32(&ids[hashRoute(r, protocol)%buckets], 1)
|
||||
}
|
||||
// ip首部编码
|
||||
ip.Encode(&header.IPv4Fields{
|
||||
IHL: header.IPv4MinimumSize,
|
||||
TotalLength: length,
|
||||
ID: uint16(id),
|
||||
TTL: ttl,
|
||||
Protocol: uint8(protocol),
|
||||
SrcAddr: r.LocalAddress,
|
||||
DstAddr: r.RemoteAddress,
|
||||
})
|
||||
// 计算校验和和设置校验和
|
||||
ip.SetChecksum(^ip.CalculateChecksum())
|
||||
r.Stats().IP.PacketsSent.Increment()
|
||||
|
||||
// 写入网卡接口
|
||||
if protocol == header.ICMPv4ProtocolNumber {
|
||||
log.Printf("IP 写回ICMP报文 长度: %d\n", hdr.UsedLength()+payload.Size())
|
||||
} else {
|
||||
log.Printf("send ipv4 packet %d bytes, proto: 0x%x", hdr.UsedLength()+payload.Size(), protocol)
|
||||
}
|
||||
return e.linkEP.WritePacket(r, hdr, payload, ProtocolNumber)
|
||||
}
|
||||
|
||||
// HandlePacket is called by the link layer when new ipv4 packets arrive for
|
||||
// this endpoint.
|
||||
// 收到ip包的处理
|
||||
func (e *endpoint) HandlePacket(r *stack.Route, vv buffer.VectorisedView) {
|
||||
// 得到ip报文
|
||||
h := header.IPv4(vv.First())
|
||||
// 检查报文是否有效
|
||||
if !h.IsValid(vv.Size()) {
|
||||
return
|
||||
}
|
||||
log.Println(h)
|
||||
|
||||
hlen := int(h.HeaderLength())
|
||||
tlen := int(h.TotalLength())
|
||||
vv.TrimFront(hlen)
|
||||
vv.CapLength(tlen - hlen)
|
||||
|
||||
// 报文重组 TODO
|
||||
more := (h.Flags() & header.IPv4FlagMoreFragments) != 0
|
||||
// 是否需要ip重组
|
||||
if more || h.FragmentOffset() != 0 {
|
||||
// The packet is a fragment, let's try to reassemble it.
|
||||
last := h.FragmentOffset() + uint16(vv.Size()) - 1
|
||||
var ready bool
|
||||
log.Println(last)
|
||||
// ip分片重组
|
||||
//vv, ready = e.fragmentation.Process(hash.IPv4FragmentHash(h), h.FragmentOffset(), last, more, vv)
|
||||
if !ready {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 得到传输层的协议
|
||||
p := h.TransportProtocol()
|
||||
// 如果时ICMP协议,则进入ICMP处理函数
|
||||
if p == header.ICMPv4ProtocolNumber {
|
||||
e.handleICMP(r, vv)
|
||||
return
|
||||
}
|
||||
r.Stats().IP.PacketsDelivered.Increment()
|
||||
// 根据协议分发到不同处理函数,比如协议时TCP,会进入tcp.HandlePacket
|
||||
log.Printf("recv ipv4 packet %d bytes, proto: 0x%x", tlen, p)
|
||||
e.dispatcher.DeliverTransportPacket(r, p, vv)
|
||||
}
|
||||
|
||||
// Close cleans up resources associated with the endpoint.
|
||||
@@ -97,8 +172,12 @@ func (p *protocol) NewEndpoint(nicid tcpip.NICID, addr tcpip.Address, linkAddrCa
|
||||
nicid: nicid,
|
||||
id: stack.NetworkEndpointID{LocalAddress: addr},
|
||||
linkEP: linkEP,
|
||||
dispatcher: dispatcher,
|
||||
echoRequests: make(chan echoRequest, 10),
|
||||
}
|
||||
|
||||
go e.echoReplier()
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
@@ -122,9 +201,8 @@ func (p *protocol) MinimumPacketSize() int {
|
||||
|
||||
// ParseAddresses implements NetworkProtocol.ParseAddresses.
|
||||
func (*protocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) {
|
||||
//h := header.IPv4(v)
|
||||
//return h.SourceAddress(), h.DestinationAddress()
|
||||
return "", ""
|
||||
h := header.IPv4(v)
|
||||
return h.SourceAddress(), h.DestinationAddress()
|
||||
}
|
||||
|
||||
// SetOption implements NetworkProtocol.SetOption.
|
||||
|
||||
7
tcpip/network/ipv4/ipv4_test.go
Normal file
7
tcpip/network/ipv4/ipv4_test.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package ipv4_test
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestIPv4Base(t *testing.T) {
|
||||
|
||||
}
|
||||
@@ -238,10 +238,10 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address,
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
for i := range s.routeTable {
|
||||
//if (id != 0 && id != s.routeTable[i].NIC) ||
|
||||
// (len(remoteAddr) != 0 && !s.routeTable[i].Match(remoteAddr)) {
|
||||
// continue
|
||||
//}
|
||||
if (id != 0 && id != s.routeTable[i].NIC) ||
|
||||
(len(remoteAddr) != 0 && !s.routeTable[i].Match(remoteAddr)) {
|
||||
continue
|
||||
}
|
||||
|
||||
nic := s.nics[s.routeTable[i].NIC]
|
||||
if nic == nil {
|
||||
|
||||
@@ -183,6 +183,21 @@ type Route struct {
|
||||
NIC NICID // 使用的网卡设备
|
||||
}
|
||||
|
||||
// Match determines if r is viable for the given destination address.
|
||||
func (r *Route) Match(addr Address) bool {
|
||||
if len(addr) != len(r.Destination) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(r.Destination); i++ {
|
||||
if (addr[i] & r.Mask[i]) != r.Destination[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Stats 包含了网络栈的统计信息
|
||||
type Stats struct {
|
||||
// TODO 需要解读
|
||||
|
||||
Reference in New Issue
Block a user