netstack ==================================>
网络协议栈main函数路径:--src\connectivity\network\netstack\main.go
main() //--分析关键步骤
stk := tcpipstack.New([]string{ipv4.ProtocolName,ipv6.ProtocolName,arp.ProtocolName,},
[]string{icmp.ProtocolName4,tcp.ProtocolName,udp.ProtocolName,},
tcpipstack.Options{HandleLocal: true,}) //调用协议栈New,根据传入的协议初始化协议栈(ipv4、ipv6、arp、tcp、udp、icmp)
New //--third_party\golibs\github.com\google\netstack\tcpip\stack\stack.go
clock := opts.Clock //根据opt传入的时钟(超时定时用?)
s := &Stack{
transportProtocols: make(map[tcpip.TransportProtocolNumber]*transportProtocolState),
networkProtocols: make(map[tcpip.NetworkProtocolNumber]NetworkProtocol),
linkAddrResolvers: make(map[tcpip.NetworkProtocolNumber]LinkAddressResolver),
nics: make(map[tcpip.NICID]*NIC),
linkAddrCache: newLinkAddrCache(ageLimit, resolutionTimeout, resolutionAttempts),
PortManager: ports.NewPortManager(),
clock: clock,
stats: opts.Stats.FillIn(),
handleLocal: opts.HandleLocal,
raw: opts.Raw,
} //初始化构造stack
{netProto := netProtoFactory()
s.networkProtocols[netProto.Number()] = netProto
s.linkAddrResolvers[r.LinkAddressProtocol()] = r
} //循环,根据传入的proto初始化
{transProto := transProtoFactory()
} //循环,根据传入的transport初始化
s.demux = newTransportDemuxer(s) //创建全局的transport demuxer
stk.SetTransportProtocolOption //设置option
arena, err := eth.NewArena() //分配协议栈收发数据buffer vmo
NewArena //--src\connectivity\network\netstack\link\eth\arena.go
iovmo, err := zx.NewVMO() //分配2048*2048大小的vmo
iovmo.Handle().SetProperty //设置vmo名字
zx.VMARRoot.Map //映射vmo,可读可写
a := &Arena{
iovmo: iovmo,
} //构造Arena对象
devicesettings.NewDeviceSettingsManagerInterfaceRequest() //TODO
ns := &Netstack{
arena: arena, //buffer
dnsClient: dns.NewClient(stk), //dns
deviceSettings: ds, //
sniff: *sniff, //嗅探器
}
ns.addLoopback() //添加回环
ns.OnInterfacesChanged = func(){} //接口改变回调函数 TODO
netstackImpl := &netstackImpl{} //协议栈impl?
ctx.OutgoingService.AddService(){} //协议栈对外提供netstack服务
…… //dns相关,忽略!
ctx.OutgoingService.AddService(){} //协议栈对外提供StackServic服务
ctx.OutgoingService.AddService(){} //协议栈对外提供SocketProviderService服务
connectivity.AddOutgoingService(ctx) //TODO
filter.New(stk.PortManager) //过滤器(此处port是tcp层还是mac的?)
filter.AddOutgoingService(ctx, f) //TODO
go pprofListen() //TODO
fidl.Serve() //开始提供服务,run loop
NIC添加
src\virtualization\bin\vmm\device\virtio_net.cc中用到的两个协议栈接口流程
netstack_->AddEthernetDevice
AddEthernetDevice //--src\connectivity\network\netstack\netstack_service.go
ns.ns.addEth
addEth //--src\connectivity\network\netstack\netstack.go
client, err := eth.NewClient("netstack", topological_path, device, ns.arena)
NewClient //--src\connectivity\network\netstack\link\eth\client.go
device.SetClientName
device.ConfigMulticastSetPromiscuousMode(true)
device.GetInfo //调用网卡驱动通用接口层kOps.GetInfo,获取mac等信息
device.GetFifos //调用网卡驱动通用接口层kOps.GetFifos
c := &Client{}
{c.arena.iovmo.Handle().Duplicate(zx.RightSameRights)
device.SetIoBuffer(zx.VMO(h)) //调用网卡驱动通用接口层kOps.SetIoBuffer
c.rxCompleteLocked()
{c.arena.alloc(c)
buf = append(buf, c.arena.entry(b))
} //循环RxDepth,为rxfifo在arena上分配空间,并追加到buf后面
fifoWrite(c.fifos.Rx, buf) //向fifo中写入数据,通知网卡驱动通用接口层
zx.Sys_fifo_write
c.arena.free
}
ns.addEndpoint(func(nicid tcpip.NICID) string {}, eth.NewLinkEndpoint(client), client, true, routes.Metric(config.Metric))
NewLinkEndpoint //--src\connectivity\network\netstack\link\eth\endpoint.go
&endpoint{client: client} //
addEndpoint
linkID := stack.RegisterLinkEndpoint(ep)
RegisterLinkEndpoint //--third_party\golibs\github.com\google\netstack\tcpip\stack\registration.go
v := nextLinkEndpointID //全局变量
linkEndpoints[v] = linkEP //全局map,保存所有link层的ep
linkID = sniffer.New(linkID) //嗅探器包装一层,同样会调用RegisterLinkEndpoint注册,分析TODO
linkID, ifs.filterEndpoint = filter.NewFilterEndpoint(ns.filter, linkID) //filter又包装一层,同样会调用RegisterLinkEndpoint注册,分析TODO
linkID, ifs.bridgeable = bridge.NewEndpoint(linkID) //bridge包装下,下面创建NIC的linkID是bridge的linkID,所以,所有到NIC的包都要经过bridge处理一下,再分发
stack.RegisterLinkEndpoint(e) //bridge的ep也关联到此NIC,同时保存到全局map
ns.mu.stack.CreateNIC(ifs.nicid, linkID)
CreateNIC //--third_party\golibs\github.com\google\netstack\tcpip\stack\stack.go
s.createNIC
ep := FindLinkEndpoint(linkEP)
newNIC(s, id, name, ep, loopback) //--third_party\golibs\github.com\google\netstack\tcpip\stack\nic.go
return &NIC{……} //构造NIC(network interface card,是协议栈attach的对象)
s.nics[id] = n //将n添加到stack的nics数组中
n.attachLinkEndpoint() //addtach NIC to endpoint,会使能收发包
n.linkEP.Attach(n) 【//这里Attach就有多种实现(参见netstack\tcpip\link目录),这里是以fdbased实现为例;启动一个goroutine从fd中读取包,并通过dispatcher分发出去。
e.dispatcher = dispatcher
go e.dispatchLoop() //分发loop
e.inboundDispatcher()
e.inboundDispatcher = e.packetMMapDispatch
e.inboundDispatcher = e.recvMMsgDispatch
e.inboundDispatcher = e.dispatch //非socketfd走此路径,此处以此为例
n, err := rawfile.BlockingReadv(e.fd, e.iovecs[0])
e.dispatcher.DeliverNetworkPacket(e, remote, local, p, vv)
DeliverNetworkPacket() //详见nic.go文件分析】 //这里分析可能有误
【Attach //--src\connectivity\network\netstack\link\eth\endpoint.go
go func() {……} //新启一个goroutine来分发
b, err := e.client.Recv() //调用接收
v := append(buffer.View(nil), b...)
eth := header.Ethernet(v) //获取eth头
dispatcher.DeliverNetworkPacket(……) //调用ep对应的dispatcher分发处理数据包
e.dispatcher = dispatcher】 //这里分析也是错误的,应该是bridge的attach
Attach //--src\connectivity\network\netstack\link\bridge\bridgeable.go
e.dispatcher = d //将bridge ep的dispatcher赋值为NIC
e.LinkEndpoint.Attach(e) //将NIC filter层的ep attach到bridge,这里应该也会递归触发sniffer的attach
ns.mu.stack.AddAddress(ifs.nicid, arp.ProtocolNumber, arp.ProtocolAddress) //设置arp
lladdr := header.LinkLocalAddr(linkAddr)
mu.stack.AddAddress(ifs.nicid, ipv6.ProtocolNumber, lladdr)
snaddr := header.SolicitedNodeAddr(lladdr)
ns.mu.stack.AddAddress(ifs.nicid, ipv6.ProtocolNumber, snaddr)
dhcp.NewClient //TODO
netstack_->SetInterfaceAddress
SetInterfaceAddress //--src\connectivity\network\netstack\netstack_service.go
nic := tcpip.NICID(nicid) //根据nicid获取nic
ni.ns.validateInterfaceAddress //检查给定ipaddr有效性,并返回protocol、addr
ni.ns.addInterfaceAddress
addInterfaceAddress //src\connectivity\network\netstack\netstack.go
toSubnet //获取子网
route := subnetRoute(addr, subnet.Mask(), nic) //
ns.AddRouteLocked
AddRoutesLocked
ns.getNetInterfaces2Locked
ns.OnInterfacesChanged(interfaces)
---------》这个下一步是分析物理网卡netcfg注册过程
TCP发送流程
third_party\golibs\github.com\google\netstack\tcpip\transport\tcp\snd.go
sendData
maybeSendSegment
sendSegment
sendSegmentFromView
sendRaw --connect.go
sendTCP
r.WritePacket(gso, hdr, data, ProtocolNumber, ttl)
WritePacket --third_party\golibs\github.com\google\netstack\tcpip\stack\route.go
r.ref.ep.WritePacket //TODO,后面的分析不对,到此截止。
e.linkEP.WritePacket --third_party\golibs\github.com\google\netstack\tcpip\link\fdbased\endpoint.go
rawfile.NonBlockingWrite3 --third_party\golibs\github.com\google\netstack\tcpip\link\rawfile\rawfile_unsafe.go
NonBlockingWrite
syscall.RawSyscall(syscall.SYS_WRITE, uintptr(fd), uintptr(ptr), uintptr(len(buf)))
--------------------------------------------------------------
初步分析:third_party\golibs\github.com\google\netstack\tcpip\link目录为netstack网络协议栈mac层(数据链路层)协议实现!下面对此文件夹内文件功能分析。
1.fdbased/endpoint.go<WritePacket>
WritePacket
{eth := header.Ethernet() //构造一个Ethernet头
ethHdr := &header.EthernetFields{} //构造EthernetFields结构体(14个字节头:src<未赋值>、dest<赋值为r.RemoteLinkAddress>、type<赋值为入参protocol>)赋值给ethHdr
ethHdr.SrcAddr //对src赋值,如果r.LocalLinkAddress有值则取它,否则取调用者e.addr
eth.Encode(ethHdr) //对头进行编码 }//e.hdrSize > 0
{……//gso填充 TODO
rawfile.NonBlockingWrite3 //调用rawfile包的NonBlockingWrite3函数
NonBlockingWrite3 //--link/rawfile/rawfile_unsafe.go
[NonBlockingWrite]
iovec := [3]syscall.Iovec{……} //构造iovec结构体数组,每个结构体包含base和len两个元素
syscall.RawSyscall //系统调用怎么实现TODO
}//e.Capabilities()&stack.CapabilityGSO != 0
2.loopback/loopback.go //对上层传下来的包不加mac头,直接又返回上层dispatcher处理
3.sharedmem/sharedmem.go //共享内存发mac包
WritePacket
eth := header.Ethernet() //构造一个Ethernet头
ethHdr := &header.EthernetFields{} //构造EthernetFields结构体(14个字节头:src<未赋值>、dest<赋值为r.RemoteLinkAddress>、type<赋值为入参protocol>)赋值给ethHdr
ethHdr.SrcAddr //对src赋值,如果r.LocalLinkAddress有值则取它,否则取调用者e.addr
eth.Encode(ethHdr) //对头进行编码
v := payload.ToView() //payload为buffer.VectorisedView类型
ToView //--tcpip\buffer\view.go 返回其View(View is a slice of a buffer, with convenience methods)
e.tx.transmit(hdr.View(), v) //e.mu.Lock锁保护;hdr类型为buffer.Prependable,是一个向前增长的buffer,方便在buffer前端加上各层协议头
id, ok := t.q.CompletedPacket //返回最后完成的transmission的id
buf := t.ids.remove(id) //移除id及关联的buffer,以便重用
t.bufs.free //释放buffer
t.bufs.alloc //从manager处,循环分配足够的buffer来装数据
copy(dBuf, data) //拷贝数据(入参a、b)到前面分配的buffer
t.ids.add //从endpoint获取一个id
t.q.Enqueue //发送packet?
t.tx.Push(totalLen) //压入总长度
binary.LittleEndian.PutUint64(b[packetID:], id) //初始化packetID
binary.LittleEndian.PutUint32(b[packetSize:], totalDataLen)//初始化packetSize
binary.LittleEndian.PutUint32(b[packetReserved:], 0)//初始化packetReserved
{ binary.LittleEndian.PutUint64(b[offset+bufferOffset:], buffer.Offset)
binary.LittleEndian.PutUint32(b[offset+bufferSize:], buffer.Size)}//循环
t.tx.Flush() //flush cache到内存,接收端可以读取数据了
4.channel/channel.go
WritePacket
p := PacketInfo{} //构造PacketInfo,包含header、payload、protocol和gso;貌似没有填入mac到header?!
e.C <- p //这是什么操作符?将p写入endpoint的channel?
5.sniffer/sniffer.go //嗅探器,抓包工具
WritePacket //它实现自stack.LinkEndpoint interface,仅仅是记录下包信息,并把包传递给lower endpoint
6.muxed/injectable.go //把包发给远端地址的可注入端点,只用于远端地址端点有路由器注册情况
WritePacket
endpoint, ok := m.routes[r.RemoteAddress]
endpoint.WritePacket
7.waitable/waitable.go //Wait or WaitWrite没被调用情况下,直接传递给lower.WritePacket;否则,返回nil
------------------------------
端点(endpoint)管理
-----------------------------------------------
ifconfig-bridge 网桥
func main()--src\connectivity\network\netstack\ifconfig\ifconfig.go
switch os.Args[1] //根据传入的参数分别处理
case "bridge"
ifaces := os.Args[2:] //将第二个以及后面的参数构造字符串ifaces
nicid, err := a.bridge(ifaces)
ifs := make([]*netstack.NetInterface2, len(ifNames)) //根据接口数分配ifs内存
nicIDs := make([]uint32, len(ifNames)) //根据接口数分配nicID内存
ifaces, err := a.netstack.GetInterfaces2() //获取所有注册的iface
for i, ifName := range ifNames {
iface := getIfaceByNameFromIfaces(ifName, ifaces)
if iface == nil {
return 0, fmt.Errorf("no such interface '%s'\n", ifName)
}
ifs[i] = iface
nicIDs[i] = iface.Id
} //根据提供的接口名(ifNames),找到所有的iface实体,并进行保存赋值
result, nicid, _ := a.netstack.BridgeInterfaces(nicIDs) //bridge重点函数:根据接口构造网桥 --src\connectivity\network\netstack\netstack_service.go
nics := make([]tcpip.NICID, len(nicids)) //分配内存
for i, n := range nicids {
nics[i] = tcpip.NICID(n)
} //数组赋值
ifs, err := ni.ns.Bridge(nics) //--src\connectivity\network\netstack\netstack.go
links := make([]*bridge.BridgeableEndpoint, 0, len(nics)) //分配内存
for _, nicid := range nics {
ifs, ok := ns.mu.ifStates[nicid] //构造ifstate
if !ok {
panic("NIC known by netstack not in interface table")
}
if err := ifs.eth.SetPromiscuousMode(true); err != nil {
return nil, err
} //设置混杂模式
links = append(links, ifs.bridgeable) //构造links
}
b := bridge.New(links) //--src\connectivity\network\netstack\link\bridge\bridge.go
ep := &Endpoint{links: make(map[tcpip.LinkAddress]*BridgeableEndpoint)} //构造ep
for _, l := range links {
ep.links[linkAddress] = l //linkAddress为mac地址,将mac与BridgeableEndpoint(关联一个NIC)做好映射
…… //MTUs、capabilities取最小值,maxHeaderLength取最大值
}
ep.linkAddress = tcpip.LinkAddress(b) //算法生成bridge的mac地址
ns.addEndpoint //将网桥加入endpoint
linkID := stack.RegisterLinkEndpoint(ep)
RegisterLinkEndpoint //--third_party\golibs\github.com\google\netstack\tcpip\stack\registration.go
v := nextLinkEndpointID //全局变量
linkEndpoints[v] = linkEP //全局map
linkID = sniffer.New(linkID) //嗅探器包装一层
linkID, ifs.bridgeable = bridge.NewEndpoint(linkID) //bridge包装下,
stack.RegisterLinkEndpoint(e)
ns.mu.stack.CreateNIC(ifs.nicid, linkID)
CreateNIC //--third_party\golibs\github.com\google\netstack\tcpip\stack\stack.go
s.createNIC
ep := FindLinkEndpoint(linkEP)
newNIC(s, id, name, ep, loopback) //--third_party\golibs\github.com\google\netstack\tcpip\stack\nic.go
return &NIC{……} //构造NIC(network interface card,是协议栈attach的对象)
s.nics[id] = n //将n添加到stack的nics数组中
n.attachLinkEndpoint() //addtach NIC to endpoint,会使能收发包
n.linkEP.Attach(n)
ep.dispatcher = d //将此ep的包分发对象设置为bridge--src\connectivity\network\netstack\link\bridge\bridge.go
interfaces, _ := a.netstack.GetInterfaces2()
bridge := getIfaceByIdFromIfaces(uint32(nicid), interfaces)
//启动bridge
func (ep *Endpoint) Up() //--src\connectivity\network\netstack\link\bridge\bridge.go
for _, l := range ep.links {
l.SetBridge(ep) //将此桥的所有links的桥服务设置为此桥,此动作之后,到达任意links的包都要转发到bridge来处理
}
onStateChange(link.StateStarted)
DeliverNetworkPacket
if l, ok := ep.links[dstLinkAddr]; ok {
l.Dispatcher().DeliverNetworkPacket(l, srcLinkAddr, dstLinkAddr, p, vv)
return
} //根据包的目的mac地址,调用对应NIC的dispatcher的DeliverNetworkPacket函数进行分发包处理
r := stack.Route{LocalLinkAddress: srcLinkAddr, RemoteLinkAddress: dstLinkAddr, NetProto: p} //构造route对象
rxaddr := rxEP.LinkAddress() //记录包的入口mac地址
for linkaddr, l := range ep.links {
if linkaddr != rxaddr { //遍历网桥的所有link点(入口除外),调用其WritePacket函数,最终会调用client的send函数fifoWrite
l.WritePacket(&r, nil, hdr, payload, p)
}
}
//src\connectivity\network\netstack\link\bridge\bridgeable.go
DeliverNetworkPacket
b := e.mu.bridge
b.DeliverNetworkPacket //设置bridge的情况,调用bridge的分发函数
[e.dispatcher.DeliverNetworkPacket] //为设置bridge的情况,直接调用NIC的分发函数
----------------------------------
arena管理
//TODO:
=====================================================================部分网络协议栈文件分析
nic.go
//NIC接收到物理接口来的包处理
func (n *NIC) DeliverNetworkPacket(……)
src, dst := netProto.ParseAddresses(vv.First())
if dst == header.IPv4Broadcast {
for _, ref := range n.endpoints { //广播场景,让NIC所有关联的网络层ep都接收并处理包
r := makeRoute(protocol, dst, src, linkEP.LinkAddress(), ref, false /* handleLocal */, false /* multicastLoop */)
r.RemoteLinkAddress = remote
ref.ep.HandlePacket(&r, vv) //交给上层业务处理,HandlePacket在上层协议中定义,如tcp、udp、icmp都有自己的定义
}}
if ref := n.getRef(protocol, dst); ref != nil {……} //非广播场景,根据dst ip 获取关联referencedNetworkEndpoint,并调用其HandlePacket处理包。
if n.stack.Forwarding() {……} //本NIC不关心此包情况,找到另一个关心此包的NIC,传递给它处理。(目前貌似没使能!分析TODO)