google netstack 数据链路层分析

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)


 

上一篇:TCPIP卷一(1):二层封装与路由器转发数据过程


下一篇:TCPIP实验1、2(面向连接的Socket编程)