1、网络初始化linux
一、hyperd/daemon/daemon.gojson
func NewDaemon(cfg *apitypes.HyperConfig) (*Daemon, error)api
该函数直接调用daemon.initNetworks(cfg)bash
二、hyperd/daemon/daemon.go网络
func (daemon *Daemon) initNetworks(c *apitypes.HyperConfig) error数据结构
该函数仅仅只是调用hypervisor.InitNetwork(c.Bridge, c.BridgeIP, c.DisableIptables),所以关于网络的内容都是在runv中完成的app
三、runv/hypervisor/hypervisor.godom
func InitNetwork(bIface, bIP string, disableIptables bool) erroride
若HDriver.BuildinNetwork()为true,则return HDriver.InitNetwork(bIface, bIP, disableIptables) // QEMU为false函数
不然,return network.InitNetwork(bIface, bIP, disableIptables)
四、runv/hypervisor/network/network_linux.go
func InitNetwork(bIface, bIP string, disable bool) error
(1)、首先设置BridgeIface和BridgeIP,BridgeIface默认为"hyper0",bIP默认为"192.168.123.0/24",并将disableIptables设置为disable
(2)、调用addr, err := GetIfaceAddr(BridgeIface),若err 不为nil,则说明bridge不存在,须要建立一个,不然说明bridge存在,可是仍然须要对配置信息进行匹配检查
(3)、若bridge不存在,则调用configureBridge(BridgeIP, BridgeIface)建立一个,再调用addr, err = GetIfaceAddr(BridgeIface)获取bridge信息,再调用BridgeIPv4Net = addr.(*net.IPNet)
(4)、调用setupIPTables(addr)
(5)、调用setupIPForwarding()
(6)、最后调用IpAllocator.RequestIP(BridgeIPv4Net, BridgeIPv4Net.IP)
// Return the first IPv4 address for the specified network interface
五、runv/hypervisor/network_linux.go
func GetIfaceAddr(name string) (net.Addr, error)
(1)、首先调用iface, err := net.InterfaceByName(name)以及addrs, err := iface.Addrs()获取地址信息
(2)、设置变量var addr4 []net.Addr,再从addrs中解析,最终返回addr4[0]
// create and setup network bridge
六、runv/hypervisor/network_linux.go
func configureBridge(bridgeIP, bridgeIface string) error
(1)、检测bridgeIP并将其赋值给ifaceAddr
(2)、调用CreateBridgeIface(bridgeIface),并忽略已经"exists"的错误
(3)、调用iface, err := net.InterfaceByName(bridgeIface)获取接口
(4)、调用ipAddr, ipNet, err := net.ParseCIDR(ifaceAddr) (注:For example, ParseCIDR("198.51.100.1/24") returns the IP address 198.51.100.1 and the network 198.51.100.0/24.)
(5)、若ipAddr.Equal(ipNet.IP)则调用ipAddr, err = IpAllocator.RequestIP(ipNet, nil)
不然调用ipAddr, err = IpAllocator.RequestIP(ipNet, ipAddr)
(6)、调用NetworkLinkAddIp(iface, ipAddr, ipNet)
(7)、调用NetworkLinkUp(iface) ---> 都是对进行底层的syscall.Syscall()的调用
// Create the actual bridge device. This is more backward-compatible than netlink and works on RHEL 6.
七、runv/hypervisor/network_linux.go
func CreateBridgeIface(name string) error
该函数进行最底层的syscall.Syscall(...)来建立网桥
IPAllocator结构以下所示:
type IPAllocator struct { allocatedIPs networkSet mutex sync.Mutex }
networkSet的定义以下所示:
type networkSet map[string]*allocatedMap
allocatedMap结构以下所示:
type allocatedMap struct { p map[string]struct{} last *big.Int begin *big.Int end *big.Int }
// 当参数ip为nil时,返回network中下一个可获取的IP地址,若是参数ip不为nil,则会校验给定的ip是否合法
八、runv/hypervisor/network/ipallocator/ipallocator.go
func (a *IPAllocator) RequestIP(network *net.IPNet, ip net.IP) (net.IP, error)
(1) 、调用key := network.String()返回该network的字符串表示,并调用allocated, ok := a.allocatedIPs[key]
(2)、若该network不存在,则调用allocated = newAllocatedMap(network)新建一个,并调用a.allocatedIPs[key] = allocated
(3)、若ip == nil,则调用return allocated.getNextIP(),不然调用allocated.checkIP(ip)
// This function is identical to: ip addr add $ip/$ipNet dev $iface
9、runv/hypervisor/network/network_linux.go
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error
(1)、该函数直接调用return networkLinkIpAction(syscall.RTM_NEWADDR, syscall.NLM_F_CREAT|syscall.NLM_F_EXCL|syscall.NLM_F_ACK, IfAddr{iface, ip, ipNet})
至于networkLinkIpAction(...)函数则仅仅只是利用netlink执行命令而已
八、runv/hypervisor/network_linux.go
func setupIPTables(addr net.Addr) error
(1)、Enable NAT:
`iptables -t nat -I POSTROUTING -s 192.168.123.0/24 ! -o hyper0 -j MASQUERADE`,将进入host,可是目的地不是本地其余容器的容器流量作snat
(2)、Create HYPER iptables Chain
(3)、Goto HYPER chain
`iptables -t filter -I FORWARD -o hyper0 -j HYPER`将转发到hyper0的流量交由HYPER链处理
(4)、Accept all outgoing packets
`iptables -t filter -I FORWARD -i hyper0 -j ACCEPT`从hyper0进入的流量所有接受
(5)、Accept incoming packets for existing connections
`iptables -t filter -I FORWARD -o hyper0 -m conntrack --ctstate RELATED, ESTABLISHED -j ACCETP`
(6)、在nat中,Create HYPER iptables Chain
`iptables -t nat -N HYPER`
(7)、Goto HYPER chain
`iptables -t nat -I OUTPUT -m addrtype --dst-type LOCAL ! -d 127.0.0.1/8 -j HYPER` `iptables -t nat -I PREROUTING -m addrtype --dst-type LOCAL -j HYPER`
九、runv/hypervisor/network_linux.go
func setupIPForwarding() error
(1)、Get current IPv4 forward setup
(2)、Enable IPv4 forwarding only if it is not already enabled
2、hyperd部分网络配置
// hyperd/daemon/pod/provision.go
一、func CreateXPod(factory *PodFactory, spec *apitypes.UserPod) (*Xpod, error)
...
(1)、调用p.initResources(spec, true)
(2)、调用err = p.prepareResources()
(3)、调用err = p.addResourcesToSandbox()
....
// hyperd/daemon/pod/provision.go
二、func (p *XPod) initResources (spec *apitypes.UserPod, allowCreate bool) error
....
(1)、当len(spec.Interfaces) == 0时,调用spec.Interfaces = append(spec.Interfaces, &apitypes.UserInterface{})
(2)、遍历spec.Interfaces,调用inf := newInterface(p, nspec)和p.interfaces[nspec.Ifname] = inf
其中newInterface()函数仅仅返回一个Interface{}结构,若是spec.Ifname为""时,将其设置为"eth-default"
....
Interface{}的数据结构以下所示:
type Interface struct { p *XPod spec *apitypes.UserInterface descript *runv.InterfaceDescription }
apitypes.UserInterface 结构以下所示:
type UserInterface struct { Bridge string Ip string Ifname string Mac string Gateway string Tap string }
// hyperd/daemon/pod/provision.go
三、func (p *XPod) prepareResources() error
....
(1)、遍历p.interfaces,调用inf.prepare()
....
// hyperd/daemon/pod/networks.go
四、func (inf *Interface) prepare() error
(1)、当inf.spec.Ip == ""而且inf.spec.Bridge != ""时报错 --> if configured a bridg, must specify the IP address
(2)、当inf.spec.Ip == ""时,调用setting, err := network.AllocateAddr(""),而且用setting的内容填充&runv.InterfaceDescription{}结构,并赋值给inf.descript
不然,直接将用inf的内容填充&runv.InterfaceDescription{}结构,并赋值给inf.descript
// runv/hypervisor/network/network_linux.go
五、func AllocateAddr(requestedIP string) (*Settings, error)
(1)、调用ip, err := IpAllocator.RequestIP(BridgeIPv4Net, net.parseIP(requestedIP))
(2)、调用maskSize, _ := BridgeIPv4Net.Mask.Size()以及mac, err := GenRandomMac()
(3)、返回return &Settings{...}
// hyperd/daemon/pod/provision.go
// addResourcesToSandbox() add resources to sandbox parallelly, it issues runV API parallelly to send the
// NIC, Vols, and Containers to sandbox
六、func (p *XPod) addResourcesToSandbox() error
...
(1)、调用future := utils.NewFutureSet()
(2)、调用函数future.Add("addInterface", func() error {}),其中在func函数中调用for _, inf := range p.interfaces,并调用err := inf.add()
在遍历完p.interfaces以后,再调用p.sandbox.AddRoute()
...
// hyperd/daemon/pod/networks.go
func (inf *Interface) add() error
(1)、若inf.descript == nil 或者inf.descript.Ip 为"",则报错
(2)、调用inf.p.sandbox.AddNic(inf.descript)
3、runv部分网络配置
(1)、添加网卡
网卡的数据结构以下所示:
type InterfaceDescription struct { Id string Lo bool Bridge string Ip string Mac string Gw string TapName string
Options string }
// runv/hypervisor/vm.go
一、func (vm *Vm) AddNic(info *api.InterfaceDescription)
(1)、设置client := make(chan api.Result, 1),用于同步
(2)、调用vm.ctx.AddInterface(info, client)
(3)、调用ev, ok := <-client等待网卡建立完成
(3)、调用return vm.ctx.updateInterface(info.Id)
// runv/hypervisor/context.go
2、func (ctx *VmContext) AddInterface(inf *api.InterfaceDescription, result chan api.Result)
(1)、当ctx.current != StateRunning时,报错,调用result <- NewNotReadyError(ctx.Id)
(2)、调用ctx.networks.addInterface(inf, result)
// runv/hypervisor/network.go
二、func (nc *NetworkContext) addInterface(inf *api.InterfaceDescription, result chan api.Result)
(1)、当inf.Lo为true时,填充i := &InterfaceCreated{...},nc.lo[inf.Ip] = i, nc.idMap[inf.Id] = i,并成功返回
(2)、启动一个goroutine,调用idx := nc.applySlot(),获取interface对应的ethernet slot
nc.configureInterface(idx, nc.sandbox.netPciAddr(), fmt.Sprintf("eth%d", idx), inf, devChan)
(3)、启动一个goroutine,等待device inserted状况,并经过result返回网卡插入成功或者失败的信息
// runv/hypervisor/network.go
三、func (nc *NetworkContext) configureInterface(index, pciAddr int, name string, inf *api.InterfaceDescription, result chan<- VmEvent)
(1)、调用settings, err = network.Configure(nc.sandbox.Id, "", false, inf)
(2)、调用created, err := interfaceGot(inf.Id, index, pciAddr, name)
(3)、用created填充h := &HostNicInfo{}和g := &GuestNicInfo{}
(4)、调用nc.eth[index] = created以及nc.idMap[created.Id] = created
(5)、最后调用nc.sandbox.DCtx.AddNic(nc.sandbox, h, g, result)
HostNicInfo结构以下所示:
type HostNicInfo struct { Id string Fd uint64 Device string Mac string Bridge string Gateway string }
GuestNicInfo结构以下所示:
type GuestNicInfo struct { Device string Ipaddr string Index int Busaddr int }
// runv/hypervisor/network/network_linux.go
四、func configure(vmId, requestIP string, addrOnly bool, inf *api.InterfaceDescription) (*Settings, error)
(1)、调用ip, mask, err := ipParser(inf.Ip)获取配置的ip,再调用maskSize, _ := mask.Size()获取mask的长度
(2)、调用mac := inf.Mac,若是mac为"",则调用mac, err := GenRandomMac()建立一个
(3)、若是addrOnly为True,则return &Settings{...},其中Device为inf.TapName, File为nil,
(4)、不然调用device, tapFile, err := GetTapFd(inf.TapName, inf.Bridge, inf.Options),GetTapFd建立一个tap设备,将它加到bridge中,并启动
最终return &Settings{...},其中Device为device, File为tapFile
Settings结构以下所示:
type Settings struct { Mac string IPAddress string IPPrefixLen int Gateway string Bridge string Device string File *os.File Automatic bool }
// runv/hypervisor/network.go
五、func interfaceGot(id string, index int, pciAddr int, name string, inf *network.Settings) (*InterfaceCreated, error)
(1)、调用ip, nw, err := net.ParseCIDR(fmt.Sprintf("%s/%d", inf.IPAddress, inf.IPPrefixLen))
(2)、建立rt := []*RouteRule{},若是该interface为第一个且inf.Automatic为true(默认为false),或者配有gateway且inf.Automatic为false,则建立相应的RouteRule,调用:
rt = append(rt, &RouteRule{Destination: "0.0.0.0/0", Gateway: inf.Gateway, ViaThis: true,})
(3)、最后return &InterfaceCreated{}, nil
InterfaceCreated结构以下所示:
type InterfaceCreated struct { Id string Index int PCIAddr int Fd *os.File Bridge string HostDevice string DeviceName string MacAddr string IpAddr string NetMask string RouteTable []*RouteRule }
RouteRule结构以下所示:
type RouteRule struct { Destination string Gateway string ViaThis bool }
// 当虚拟机驱动为QEMU时
// runv/hypervisor/qemu/qemu.go
六、func (qc *QemuContext) AddNic(ctx *hypervisor.VmContext, host *hypervisor.HostNicInfo, guest *hypervisor.GuestNicInfo, result chan <- hypervisor.VmEvent)
该函数直接调用newNetworkAddSession(ctx, qc, host.Id, host.Fd, guest.Device, host.Mac, guest.Index, guest.Busaddr, result)
// runv/hypervisor/qemu/qmp_wrapper_amd64.go
七、func newNetworkAddSession(ctx *hypervisor.VmContext, qc *QemuContext, id string, fd uint64, device, mac string, index, addr int, result chan<- hypervisor.VmEvent)
(1)、先建立"getfd","netdev_add"和"device_add"三个QmpCommand命令
(2)、再将这三个命令组建成一个QmpSession,发送给QEMU
// runv/hypervisor/vm_states.go
八、func (ctx *VmContext) updateInterface(id string) error
(1)、首先调用inf := ctx.networks.getInterface(id)获取建立的interface
(2)、若inf不为nil,则调用ctx.hyperstart.UpdateInterface(inf.DeviceName, inf.IpAddr, inf.NetMask)
// runv/hyperstart/libhyperstart/json.go
九、func (h *jsonBasedHyperstart) UpdateInterface(dev, ip, mask string) error
该函数直接调用return h.hyperstartCommand(hyperstartapi.INIT_SETUPINTERFACE, hyperstartapi.NetworkInf{Device: dev, IpAddress: ip, NetMask: mask})
hyperstartCommand()进一步调用hyperstartCommandWithRetMsg(),最后建立hyperstartCmd{}将请求发给QEMU完成命令
(2)、添加路由
// runv/hypervisor/vm.go
一、func (vm *Vm) AddRoute()
(1)、调用routes := vm.ctx.networks.getRoutes()获取路由信息
(2)、再调用return vm.ctx.hyperstart.AddRoute(routes)添加路由
2、func (h *jsonBasedHyperstart) AddRoute(r []hyperstartapi.Route) error
该函数仅仅调用return h.hyperstartCommand(hyperstartapi.INIT_SETUPROUTE, hyperstartapi.Routes{Routes: r}),具体操做和更新网卡信息时相同
4、hyperstart的网络配置
// hyperstart/src/init.c
1、static int hyper_setup_pod(struct hyper_pod *pod)
...
(1)、调用hyper_setup_network(pod)
...
// hyperstart/src/net.c
2、int hyper_setup_network(struct hyper_pod *pod)
(1)、首先调用hyper_rescan()
(2)、建立变量struct rtnl_handle rth,并调用netlink_open(&rth)
(3)、建立for循环遍历pod->iface[],调用ret = hyper_setup_interface(&rth, iface, pod)配置网卡
(4)、调用ret = hyper_up_nic(&rth, 1)启动lo
(5)、建立for循环遍历pod->rt[],调用ret = hyper_setup_route(&rth, rt, pod)建立路由
注意:网卡和路由既能够在建立pod时设置,也能够单独设置,经过直接给hyperstart发送cmd
最终在hyperstart中经过hyper_cmd_setup_interface和hyper_cmd_setup_route完成(二者再直接调用hyper_setup_interface和hyper_setup_route)
hyper_interface结构以下所示:
struct hyper_interface { char *device; struct list_head ipaddresses; char *new_device_name; unsigned int mtu; }
hyper_route结构以下所示:
struct hyper_route { char *dst; char *gw; char *device; }
// hyperstart/src/net.c
三、static int hyper_setup_interface(struct rtnl_handler *rth, struct hyper_interface *iface, struct hyper_pod *pod)
(1)、构建netlink request,调用ifindex = hyper_get_ifindex(iface->device, pod)获取网卡的index ---> hyper_get_index经过读取/sys/class/net/$NIC/ifindex来读取索引号
(2)、遍历iface->device,设置网卡的IP地址
(3)、若是iface->new_device_name不为空且和iface->device不一样,则调用hyper_set_interface_name()设置网卡名字
(4)、若是iface->mtu大于0,则调用hyper_set_interface_mtu设置网卡的MTU
(5)、调用hyper_up_nic(rth, ifindex)启动网卡
// hyperstart/src/net.c
四、static int hyper_setup_route(struct rtnl_handle *rth, struct hyper_route *rt, struct hyper_pod *pod)
(1)、构建netlink request
(2)、若是rt->gw不为NULL,则先调用get_addr_ipv4(...)获取网关,再经过addattr_l(...)设置网关
(3)、若是rt->device不为NULL,则先调用hyper_get_ifindex(...)获取网卡的index,再经过addattr_l(...)设置出口网络设备
(4)、若是rt->dst不为"default","any"或者"all",则说明不是默认子网,首先调用char *slash = strchr(rt->dst. '/')
以后再调用get_addr_ipv4(...),获取相应dst的IP地址,并调用addattr_l(...)添加。接着,若slash不为NULL,则调用get_netmask(...)获取子网掩码
最后调用rtnl_talk(...)设置路由