ICMP实现 ip数据报分片机制还未实现

This commit is contained in:
impact-eintr
2022-11-27 20:34:34 +08:00
parent 5947778dfa
commit 5e317b273a
9 changed files with 280 additions and 13 deletions

Binary file not shown.

2
go.mod
View File

@@ -1,3 +1,3 @@
module netstack module netstack
go 1.18 go 1.19

View File

@@ -2,6 +2,7 @@ package header
import ( import (
"encoding/binary" "encoding/binary"
"fmt"
"netstack/tcpip" "netstack/tcpip"
) )
@@ -106,6 +107,12 @@ const (
IPv4Any tcpip.Address = "\x00\x00\x00\x00" 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 { func IPVersion(b []byte) int {
if len(b) < versIHL+1 { if len(b) < versIHL+1 {
return -1 return -1
@@ -216,6 +223,11 @@ func (b IPv4) SetDestinationAddress(addr tcpip.Address) {
copy(b[dstAddr:dstAddr+IPv4AddressSize], addr) 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. // Encode encodes all the fields of the ipv4 header.
func (b IPv4) Encode(i *IPv4Fields) { func (b IPv4) Encode(i *IPv4Fields) {
b[versIHL] = (4 << 4) | ((i.IHL / 4) & 0xf) b[versIHL] = (4 << 4) | ((i.IHL / 4) & 0xf)
@@ -264,3 +276,28 @@ func IsV4MulticastAddress(addr tcpip.Address) bool {
} }
return (addr[0] & 0xf0) == 0xe0 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())
}

View File

@@ -1,6 +1,7 @@
package fdbased package fdbased
import ( import (
"log"
"netstack/tcpip" "netstack/tcpip"
"netstack/tcpip/buffer" "netstack/tcpip/buffer"
"netstack/tcpip/header" "netstack/tcpip/header"
@@ -126,6 +127,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable,
ethHdr.SrcAddr = e.addr // 说明这是一个原始报文 ethHdr.SrcAddr = e.addr // 说明这是一个原始报文
} }
eth.Encode(ethHdr) // 将以太帧信息作为报文头编入 eth.Encode(ethHdr) // 将以太帧信息作为报文头编入
log.Println("链路层写回报文")
// 写入网卡中 // 写入网卡中
if payload.Size() == 0 { if payload.Size() == 0 {
return rawfile.NonBlockingWrite(e.fd, hdr.View()) return rawfile.NonBlockingWrite(e.fd, hdr.View())

128
tcpip/network/ipv4/icmp.go Normal file
View 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())
}

View File

@@ -1,6 +1,7 @@
package ipv4 package ipv4
import ( import (
"log"
"netstack/tcpip" "netstack/tcpip"
"netstack/tcpip/buffer" "netstack/tcpip/buffer"
"netstack/tcpip/header" "netstack/tcpip/header"
@@ -30,7 +31,12 @@ type endpoint struct {
id stack.NetworkEndpointID id stack.NetworkEndpointID
// 链路端的表示 // 链路端的表示
linkEP stack.LinkEndpoint 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. // DefaultTTL is the default time-to-live value for this endpoint.
@@ -73,13 +79,82 @@ func (e *endpoint) MaxHeaderLength() uint16 {
// 将传输层的数据封装加上IP头并调用网卡的写入接口写入IP报文 // 将传输层的数据封装加上IP头并调用网卡的写入接口写入IP报文
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView, func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload buffer.VectorisedView,
protocol tcpip.TransportProtocolNumber, ttl uint8) *tcpip.Error { 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 // HandlePacket is called by the link layer when new ipv4 packets arrive for
// this endpoint. // this endpoint.
// 收到ip包的处理 // 收到ip包的处理
func (e *endpoint) HandlePacket(r *stack.Route, vv buffer.VectorisedView) { 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. // Close cleans up resources associated with the endpoint.
@@ -94,11 +169,15 @@ type protocol struct{}
func (p *protocol) NewEndpoint(nicid tcpip.NICID, addr tcpip.Address, linkAddrCache stack.LinkAddressCache, func (p *protocol) NewEndpoint(nicid tcpip.NICID, addr tcpip.Address, linkAddrCache stack.LinkAddressCache,
dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint) (stack.NetworkEndpoint, *tcpip.Error) { dispatcher stack.TransportDispatcher, linkEP stack.LinkEndpoint) (stack.NetworkEndpoint, *tcpip.Error) {
e := &endpoint{ e := &endpoint{
nicid: nicid, nicid: nicid,
id: stack.NetworkEndpointID{LocalAddress: addr}, id: stack.NetworkEndpointID{LocalAddress: addr},
linkEP: linkEP, linkEP: linkEP,
dispatcher: dispatcher,
echoRequests: make(chan echoRequest, 10),
} }
go e.echoReplier()
return e, nil return e, nil
} }
@@ -122,9 +201,8 @@ func (p *protocol) MinimumPacketSize() int {
// ParseAddresses implements NetworkProtocol.ParseAddresses. // ParseAddresses implements NetworkProtocol.ParseAddresses.
func (*protocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) { func (*protocol) ParseAddresses(v buffer.View) (src, dst tcpip.Address) {
//h := header.IPv4(v) h := header.IPv4(v)
//return h.SourceAddress(), h.DestinationAddress() return h.SourceAddress(), h.DestinationAddress()
return "", ""
} }
// SetOption implements NetworkProtocol.SetOption. // SetOption implements NetworkProtocol.SetOption.

View File

@@ -0,0 +1,7 @@
package ipv4_test
import "testing"
func TestIPv4Base(t *testing.T) {
}

View File

@@ -238,10 +238,10 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address,
defer s.mu.RUnlock() defer s.mu.RUnlock()
for i := range s.routeTable { for i := range s.routeTable {
//if (id != 0 && id != s.routeTable[i].NIC) || if (id != 0 && id != s.routeTable[i].NIC) ||
// (len(remoteAddr) != 0 && !s.routeTable[i].Match(remoteAddr)) { (len(remoteAddr) != 0 && !s.routeTable[i].Match(remoteAddr)) {
// continue continue
//} }
nic := s.nics[s.routeTable[i].NIC] nic := s.nics[s.routeTable[i].NIC]
if nic == nil { if nic == nil {

View File

@@ -183,6 +183,21 @@ type Route struct {
NIC NICID // 使用的网卡设备 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 包含了网络栈的统计信息 // Stats 包含了网络栈的统计信息
type Stats struct { type Stats struct {
// TODO 需要解读 // TODO 需要解读