ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

google netstack 数据链路层分析

2019-08-24 10:03:38  阅读:302  来源: 互联网

标签:bridge google ep netstack go stack 链路层 tcpip


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)


 

标签:bridge,google,ep,netstack,go,stack,链路层,tcpip
来源: https://blog.csdn.net/gaojy19881225/article/details/100049134

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有