容器CNI完全解读bridge实现(二)

原创 2017年07月17日 14:46:35

之前介绍CNI基本操作,现在介绍一个bridge的实现。
它也实现了创建和删除接口。
先看创建接口:

func cmdAdd(args *skel.CmdArgs) error {
    n, cniVersion, err := loadNetConf(args.StdinData)
    if err != nil {
        return err
    }

    if n.IsDefaultGW {
        n.IsGW = true
    }

    br, brInterface, err := setupBridge(n)
    if err != nil {
        return err
    }

    netns, err := ns.GetNS(args.Netns)
    if err != nil {
        return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
    }
    defer netns.Close()

    hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode)
    if err != nil {
        return err
    }

    // run the IPAM plugin and get back the config to apply
    r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
    if err != nil {
        return err
    }

    // Convert whatever the IPAM result was into the current Result type
    result, err := current.NewResultFromResult(r)
    if err != nil {
        return err
    }

    if len(result.IPs) == 0 {
        return errors.New("IPAM plugin returned missing IP config")
    }

    result.Interfaces = []*current.Interface{brInterface, hostInterface, containerInterface}

    for _, ipc := range result.IPs {
        // All IPs currently refer to the container interface
        ipc.Interface = 2
        if ipc.Gateway == nil && n.IsGW {
            ipc.Gateway = calcGatewayIP(&ipc.Address)
        }
    }

    if err := netns.Do(func(_ ns.NetNS) error {
        // set the default gateway if requested
        if n.IsDefaultGW {
            for _, ipc := range result.IPs {
                defaultNet := &net.IPNet{}
                switch {
                case ipc.Address.IP.To4() != nil:
                    defaultNet.IP = net.IPv4zero
                    defaultNet.Mask = net.IPMask(net.IPv4zero)
                case len(ipc.Address.IP) == net.IPv6len && ipc.Address.IP.To4() == nil:
                    defaultNet.IP = net.IPv6zero
                    defaultNet.Mask = net.IPMask(net.IPv6zero)
                default:
                    return fmt.Errorf("Unknown IP object: %v", ipc)
                }

                for _, route := range result.Routes {
                    if defaultNet.String() == route.Dst.String() {
                        if route.GW != nil && !route.GW.Equal(ipc.Gateway) {
                            return fmt.Errorf(
                                "isDefaultGateway ineffective because IPAM sets default route via %q",
                                route.GW,
                            )
                        }
                    }
                }

                result.Routes = append(
                    result.Routes,
                    &types.Route{Dst: *defaultNet, GW: ipc.Gateway},
                )
            }
        }

        if err := ipam.ConfigureIface(args.IfName, result); err != nil {
            return err
        }

        if err := ip.SetHWAddrByIP(args.IfName, result.IPs[0].Address.IP, nil /* TODO IPv6 */); err != nil {
            return err
        }

        // Refetch the veth since its MAC address may changed
        link, err := netlink.LinkByName(args.IfName)
        if err != nil {
            return fmt.Errorf("could not lookup %q: %v", args.IfName, err)
        }
        containerInterface.Mac = link.Attrs().HardwareAddr.String()

        return nil
    }); err != nil {
        return err
    }

    if n.IsGW {
        var firstV4Addr net.IP
        for _, ipc := range result.IPs {
            gwn := &net.IPNet{
                IP:   ipc.Gateway,
                Mask: ipc.Address.Mask,
            }
            if ipc.Gateway.To4() != nil && firstV4Addr == nil {
                firstV4Addr = ipc.Gateway
            }

            if err = ensureBridgeAddr(br, gwn, n.ForceAddress); err != nil {
                return err
            }
        }

        if firstV4Addr != nil {
            if err := ip.SetHWAddrByIP(n.BrName, firstV4Addr, nil /* TODO IPv6 */); err != nil {
                return err
            }
        }

        if err := ip.EnableIP4Forward(); err != nil {
            return fmt.Errorf("failed to enable forwarding: %v", err)
        }
    }

    if n.IPMasq {
        chain := utils.FormatChainName(n.Name, args.ContainerID)
        comment := utils.FormatComment(n.Name, args.ContainerID)
        for _, ipc := range result.IPs {
            if err = ip.SetupIPMasq(ip.Network(&ipc.Address), chain, comment); err != nil {
                return err
            }
        }
    }

    // Refetch the bridge since its MAC address may change when the first
    // veth is added or after its IP address is set
    br, err = bridgeByName(n.BrName)
    if err != nil {
        return err
    }
    brInterface.Mac = br.Attrs().HardwareAddr.String()

    result.DNS = n.DNS

    return types.PrintResult(result, cniVersion)
}

这个里面有几个步骤
1、创建网桥并启动网桥,
setupBridge里面调用ensureBridge,先通过err := netlink.LinkAdd(br)创建网桥,然后通过 err := netlink.LinkSetUp(br)启动网桥

2.创建veth,这个是一个管道,Linux的网卡对。
hostInterface, containerInterface, err := setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode)
当前先通过hostVeth, containerVeth, err := ip.SetupVeth(ifName, mtu, hostNS)创建网卡对,一个是主机网卡一个容器网卡,并且把主机网卡设置成hairpin模式

3.通过ipam获取IP,这个是调用另一个获取IP的二进制文件ipam,这个里面返回一个网络配置列表(result.IPs)

4.配置容器内网卡,通过ipam.ConfigureIface和ip.SetHWAddrByIP配置容器的IP地址和网卡mac,其中SetHWAddrByIP通过IP地址计算出mac地址,详见
hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix)

5.配置网桥,这个里面配置网桥的IP地址和mac。最后如果可以设置ipmasq,这样容器就可以通过SNAT去连接外部网络了,这样容器就可以加入网络了

介绍完创建的过程,删除的过程就简单了。

func cmdDel(args *skel.CmdArgs) error {
    n, _, err := loadNetConf(args.StdinData)
    if err != nil {
        return err
    }

    if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
        return err
    }

    if args.Netns == "" {
        return nil
    }

    var ipn *net.IPNet
    err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
        var err error
        ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
        return err
    })
    if err != nil {
        return err
    }

    if n.IPMasq {
        chain := utils.FormatChainName(n.Name, args.ContainerID)
        comment := utils.FormatComment(n.Name, args.ContainerID)
        if err = ip.TeardownIPMasq(ipn, chain, comment); err != nil {
            return err
        }
    }

    return nil
}

这个里面显示通过ipam.ExecDel是释放IP地址的占用,然后通过 ip.DelLinkByNameAddr去删除veth,如果设置了ipmasq,这是就可以通过TeardownIPMasq删除这些iptables规则。

相关文章推荐

容器CNI完全解读(一)

CNI(Container Network Interface)容器网络接口。CNI只专注解决容器网络连接和容器销毁时的资源释放,提供一套框架,所以CNI可以支持大量不同的网络模式,并且容易实现。 ...

容器CNI完全解读calico实现(三)

上一篇介绍了bridge的实现,这里介绍一下calico的实现。和上一篇的结构一样的,先看add然后看del,具体添加网卡的代码如下:func cmdAdd(args *skel.CmdArgs) e...

kubernetes容器网络接口(CNI) midonet网络插件的设计与实现

CNI只关心容器的网络连接,在容器创建时分配网络资源,并在删除容器时删除分配的资源。因为这个焦点,CNI有广泛的支持,规格易于实现。...

Kubernetes网络接口(CNI) midonet网络插件设计与实现

先来讲讲什么是CNI? CNI(容器网络接口)是一种操作容器网络规范,包含方法规范,参数规范等。 CNI只关心容器的网络连接,在容器创建时分配网络资源,并在删除容器时删除分配的资源。因为这个焦...

16 - Docker network第二讲-容器默认网络bridge(Docker系列)

本文章来自【知识林】在Docker服务安装成功后默认有三个网络(docker network ls):C:\Users\zsl-pc>docker network ls NETWORK ID ...
  • zsl129
  • zsl129
  • 2016年12月30日 09:58
  • 692

Ios Android Hybrid app 与 Js Bridge 二 (具体实现)

写在前面的话:背景介绍:
  • jacin1
  • jacin1
  • 2014年11月16日 22:18
  • 1052

基于F340 实现Bridge功能(二):上位机应用程序编写

上位机应用程序开发环境VC++ 6.0 一、动态链接Silicon Library 1. 在该工程工作空间文件夹中,添加Silicon Library文件 2. 动态链接该library文件  ...

Linux下的虚拟Bridge实现

  • 2014年09月12日 16:42
  • 53KB
  • 下载

学容器必须懂 bridge 网络 - 每天5分钟玩转 Docker 容器技术(32)

bridge 是应用最广泛也是默认的 bridge 网络类型,必须掌握。

ie6中容器内浮动元素的border边框不完全显示的bug

ie6中容器内浮动元素的border边框不完全显示的bug html                      职位名称             招聘人数             开始时...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:容器CNI完全解读bridge实现(二)
举报原因:
原因补充:

(最多只允许输入30个字)