路由转发还是没整明白

This commit is contained in:
impact-eintr
2023-01-03 14:48:58 +08:00
parent 6156ee63cd
commit 5d0a9c3721
6 changed files with 94 additions and 37 deletions

View File

@@ -34,7 +34,7 @@ func main() {
log.Fatal("Usage: ", os.Args[0], " <tap-device> <local-address/mask> <ip-address> <local-port>")
}
logger.SetFlags(logger.IP)
//logger.SetFlags(logger.IP)
log.SetFlags(log.Lshortfile | log.LstdFlags)
tapName := flag.Arg(0)
@@ -130,7 +130,8 @@ func main() {
log.Fatal(err)
}
if err := s.AddAddress(2, proto, "192.168.1.2"); err != nil {
addr2 := tcpip.Address(net.ParseIP("192.168.1.20").To4())
if err := s.AddAddress(2, proto, addr2); err != nil {
log.Fatal(err)
}
@@ -147,7 +148,7 @@ func main() {
{
Destination: tcpip.Address(strings.Repeat("\x00", len(addr))),
Mask: tcpip.AddressMask(strings.Repeat("\x00", len(addr))),
Gateway: "",
Gateway: "", // 路由器
NIC: 1,
},
{
@@ -158,6 +159,8 @@ func main() {
},
})
s.SetForwarding(true)
done := make(chan struct{}, 2)
//logger.SetFlags(logger.TCP)
@@ -190,27 +193,10 @@ func main() {
log.Printf("客户端 建立连接\n\n客户端 写入数据\n")
cnt := 0
size := 1 << 10
for i := 0; i < 1; i++ {
size := 1 << 11
for i := 0; i < 100; i++ {
//conn.Write([]byte("Hello Netstack"))
conn.Write(make([]byte, size))
buf := make([]byte, 1024)
for {
n, err := conn.Read(buf)
if err != nil {
log.Println(err)
return
}
cnt+=n
logger.NOTICE("客户端读取", string(buf[:]))
log.Println(cnt)
if cnt == size {
logger.NOTICE("退出")
break
}
}
}
conn.Close()
@@ -244,7 +230,6 @@ func TestServerEcho(conn *TcpConn) {
}
_ = n
logger.NOTICE("服务端读取数据", string(buf[:]))
conn.Write(buf)
}
conn.ep.Close()

View File

@@ -283,7 +283,13 @@ func (n *NIC) attachLinkEndpoint() {
在上面的函数中我们开启了网卡的数据读取机制对于fdbased这个驱动而言他的实现设这样的
``` go
// Attach 启动从文件描述符中读取数据包的goroutine,并通过提供的分发函数来分发数据报
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
e.dispatcher = dispatcher // 将这张网卡注册为该链路的网络分发器
// 链接端点不可靠。保存传输端点后,它们将停止发送传出数据包,并拒绝所有传入数据包。
go e.dispatchLoop()
}
// 循环地从fd中读取数据 然后将数据报分发给协议栈
func (e *endpoint) dispatchLoop() *tcpip.Error {
@@ -778,5 +784,69 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload b
```
在前面我们提到过链路层设备拥有读写两个接口,在激活设备的时候我们执行了读取函数,现在,我们该使用写入函数了(以fdbased为例)。
``` go
// 将上层的报文经过链路层封装,写入网卡中,如果写入失败则丢弃该报文
func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable,
payload buffer.VectorisedView, protocol tcpip.NetworkProtocolNumber) *tcpip.Error {
// 如果目标地址是设备自己 那么将报文重新返回给协议栈 也就是环回
if e.handleLocal && r.LocalAddress != "" && r.LocalAddress == r.RemoteAddress {
views := make([]buffer.View, 1, 1+len(payload.Views()))
views[0] = hdr.View()
views = append(views, payload.Views()...)
vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views) // 添加报文头
e.dispatcher.DeliverNetworkPacket(e, r.RemoteLinkAddress, r.LocalLinkAddress,
protocol, vv) // 分发数据报
return nil
}
// 非本地环回数据
// 封装增加以太网头部
eth := header.Ethernet(hdr.Prepend(header.EthernetMinimumSize)) // 分配14B的内存
ethHdr := &header.EthernetFields{ // 配置以太帧信息
DstAddr: r.RemoteLinkAddress,
Type: protocol,
}
// 如果路由信息中有配置源MAC地址那么使用该地址
// 如果没有,则使用本网卡的地址
if r.LocalLinkAddress != "" {
ethHdr.SrcAddr = r.LocalLinkAddress
} else {
ethHdr.SrcAddr = e.addr
}
eth.Encode(ethHdr) // 将以太帧信息作为报文头编入
logger.GetInstance().Info(logger.ETH, func() {
log.Println(ethHdr.SrcAddr, "链路层写回以太报文 ", r.RemoteLinkAddress, " to ", r.RemoteAddress)
})
// 写入网卡中
if payload.Size() == 0 {
return rawfile.NonBlockingWrite(e.fd, hdr.View())
}
return rawfile.NonBlockingWrite2(e.fd, hdr.View(), payload.ToView())
}
```
我们现在来捋一下,除了传输层以外的数据传输机制:
首先,数据发送方将数据从应用层复制数据到传输层,在封装传输层数据之前,我们会先解析路由。
路由: 网卡-网络协议-本地IP-本地MAC-目标IP-目标MAC
先查找路由表查看路由表中是否有目标网卡如果有检查指定IP该网卡是否绑定过如果没有指定IP,就从网卡绑定的IP里找一个能用的然后本地MAC就使用这个网卡的MAC。目标IP是数据发送方提供的目标MAC通过查询arp缓存或者发送arp广播获取。
有了路由信息我们就可以执行网络层和链路层的工作了。从路由信息中获取IP的处理端点封装IP报文封装以太报文写入网卡网卡将这些数据发送出去。
目标主机网卡接受到数据时dispatchLoop中的阻塞读不再阻塞读取数据。解析以太报文获取远端IP和远端MAC在上面我们新建网卡的时候我们已经将这张网卡作为本链路的网络分发器所以调用网卡的分发函数。
``` go
case header.ARPProtocolNumber, header.IPv4ProtocolNumber:
e.dispatcher.DeliverNetworkPacket(e, remoteLinkAddr, localLinkAddr, p, vv)
```
``` go
```

View File

@@ -116,6 +116,7 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable,
}
// 封装增加以太网头部
eth := header.Ethernet(hdr.Prepend(header.EthernetMinimumSize)) // 分配14B的内存
log.Println(eth,hdr, hdr.Prepend(header.EthernetMinimumSize))
ethHdr := &header.EthernetFields{ // 配置以太帧信息
DstAddr: r.RemoteLinkAddress,
Type: protocol,

View File

@@ -108,15 +108,6 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload b
r.Stats().IP.PacketsSent.Increment()
// 写入网卡接口
if protocol == header.ICMPv4ProtocolNumber {
log.Println("IP 写回ICMP报文", header.IPv4(append(ip, payload.ToView()...)))
} else {
logger.GetInstance().Info(logger.IP, func() {
if payload.Size() == 0 {
log.Printf("发送 IP 报文 %d bytes", hdr.UsedLength()+payload.Size())
}
})
}
return e.linkEP.WritePacket(r, hdr, payload, ProtocolNumber)
}

View File

@@ -339,20 +339,24 @@ func (n *NIC) RemoveAddress(addr tcpip.Address) *tcpip.Error {
// 当前实现的网络层协议有 arp、ipv4 和 ipv6。
func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remoteLinkAddr, localLinkAddr tcpip.LinkAddress,
protocol tcpip.NetworkProtocolNumber, vv buffer.VectorisedView) {
// 检查本协议栈是否注册过该网络协议
netProto, ok := n.stack.networkProtocols[protocol]
if !ok {
n.stack.stats.UnknownProtocolRcvdPackets.Increment()
return
}
// 网络层协议状态统计
if netProto.Number() == header.IPv4ProtocolNumber || netProto.Number() == header.IPv6ProtocolNumber {
n.stack.stats.IP.PacketsReceived.Increment()
}
// 报文内容过小 判断为受损报文 丢弃
if len(vv.First()) < netProto.MinimumPacketSize() {
n.stack.stats.MalformedRcvdPackets.Increment()
return
}
// 解析源 IP 和目标IP
src, dst := netProto.ParseAddresses(vv.First())
logger.GetInstance().Info(logger.ETH, func() {
log.Printf("网卡[%v]准备从 [%s] 向 [%s] 分发数据: %v\n", linkEP.LinkAddress(), src, dst, func() []byte {
@@ -362,22 +366,26 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remoteLinkAddr, localLin
return vv.ToView()
}())
})
// 根据网络协议和数据包的目的地址,找到网络端
// 然后将数据包分发给网络层
// 根据网络协议和数据包的目的地址,找到绑定该目标地址的网络端
if ref := n.getRef(protocol, dst); ref != nil {
// 路由 源 与 目标 反转
r := makeRoute(protocol, dst, src, linkEP.LinkAddress(), ref)
r.RemoteLinkAddress = remoteLinkAddr
logger.GetInstance().Info(logger.ETH, func() {
log.Println("准备前往 IP 将本地和远端的MAC、IP 保存在路由中 以便协议栈使用",
r.LocalLinkAddress, r.RemoteLinkAddress, r.LocalAddress, r.RemoteAddress)
})
// 将数据包分发给网络层
ref.ep.HandlePacket(&r, vv)
ref.decRef()
return
}
// 如果配置了允许转发 什么意思呢
// 就是说当前网卡并没有找到目标IP 我们来试试本机的其他网卡
// 其他网卡-其他网卡上的一个可用地址-目标地址
if n.stack.Forwarding() {
r, err := n.stack.FindRoute(0, "", dst, protocol)
r, err := n.stack.FindRoute(0, dst, src, protocol) // FIXME 将dst和src调转?
if err != nil {
n.stack.stats.IP.InvalidAddressesReceived.Increment()
return
@@ -390,10 +398,12 @@ func (n *NIC) DeliverNetworkPacket(linkEP LinkEndpoint, remoteLinkAddr, localLin
// Found a NIC.
n := r.ref.nic
n.mu.RLock()
ref, ok := n.endpoints[NetworkEndpointID{dst}]
ref, ok := n.endpoints[NetworkEndpointID{dst}] // 检查这张网卡是否绑定了目标IP
n.mu.RUnlock()
if ok && ref.tryIncRef() {
ref.ep.HandlePacket(&r, vv)
logger.NOTICE("转发数据")
ref.decRef()
} else {
// n doesn't have a destination endpoint.

View File

@@ -616,7 +616,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address,
// 构建一个路由 包括 目标ip 目标mac 本地ip 本地mac
r := makeRoute(netProto, ref.ep.ID().LocalAddress, remoteAddr, nic.linkEP.LinkAddress(), ref)
r.NextHop = s.routeTable[i].Gateway
r.NextHop = s.routeTable[i].Gateway // 路由器地址
logger.GetInstance().Info(logger.IP, func() {
log.Println(r.LocalLinkAddress, r.LocalAddress, r.RemoteLinkAddress, r.RemoteAddress, r.NextHop)
})