network: Add support for macvlan driver

Add support for macvlan driver by bridging it with a macvtap or
tap+bridge and moving the mac and ip address from the
macvlan to the bridged interface.

Fixes #162

Signed-off-by: Archana Shinde <archana.m.shinde@intel.com>
This commit is contained in:
Archana Shinde 2018-07-23 13:43:23 -07:00
parent a786643d0b
commit 8847af8343
2 changed files with 291 additions and 76 deletions

View File

@ -153,6 +153,7 @@ type Endpoint interface {
HardwareAddr() string HardwareAddr() string
Type() EndpointType Type() EndpointType
PciAddr() string PciAddr() string
NetworkPair() *NetworkInterfacePair
SetProperties(NetworkInfo) SetProperties(NetworkInfo)
Attach(hypervisor) error Attach(hypervisor) error
@ -194,6 +195,14 @@ type VhostUserEndpoint struct {
PCIAddr string PCIAddr string
} }
// BridgedMacvlanEndpoint represents a macvlan endpoint that is bridged to the VM
type BridgedMacvlanEndpoint struct {
NetPair NetworkInterfacePair
EndpointProperties NetworkInfo
EndpointType EndpointType
PCIAddr string
}
// Properties returns properties for the veth interface in the network pair. // Properties returns properties for the veth interface in the network pair.
func (endpoint *VirtualEndpoint) Properties() NetworkInfo { func (endpoint *VirtualEndpoint) Properties() NetworkInfo {
return endpoint.EndpointProperties return endpoint.EndpointProperties
@ -220,6 +229,11 @@ func (endpoint *VirtualEndpoint) PciAddr() string {
return endpoint.PCIAddr return endpoint.PCIAddr
} }
// NetworkPair returns the network pair of the endpoint.
func (endpoint *VirtualEndpoint) NetworkPair() *NetworkInterfacePair {
return &endpoint.NetPair
}
// SetProperties sets the properties for the endpoint. // SetProperties sets the properties for the endpoint.
func (endpoint *VirtualEndpoint) SetProperties(properties NetworkInfo) { func (endpoint *VirtualEndpoint) SetProperties(properties NetworkInfo) {
endpoint.EndpointProperties = properties endpoint.EndpointProperties = properties
@ -234,7 +248,7 @@ func networkLogger() *logrus.Entry {
func (endpoint *VirtualEndpoint) Attach(h hypervisor) error { func (endpoint *VirtualEndpoint) Attach(h hypervisor) error {
networkLogger().WithField("endpoint-type", "virtual").Info("Attaching endpoint") networkLogger().WithField("endpoint-type", "virtual").Info("Attaching endpoint")
if err := xconnectVMNetwork(&(endpoint.NetPair), true, h.hypervisorConfig().NumVCPUs, h.hypervisorConfig().DisableVhostNet); err != nil { if err := xconnectVMNetwork(endpoint, true, h.hypervisorConfig().NumVCPUs, h.hypervisorConfig().DisableVhostNet); err != nil {
networkLogger().WithError(err).Error("Error bridging virtual endpoint") networkLogger().WithError(err).Error("Error bridging virtual endpoint")
return err return err
} }
@ -254,14 +268,14 @@ func (endpoint *VirtualEndpoint) Detach(netNsCreated bool, netNsPath string) err
networkLogger().WithField("endpoint-type", "virtual").Info("Detaching endpoint") networkLogger().WithField("endpoint-type", "virtual").Info("Detaching endpoint")
return doNetNS(netNsPath, func(_ ns.NetNS) error { return doNetNS(netNsPath, func(_ ns.NetNS) error {
return xconnectVMNetwork(&(endpoint.NetPair), false, 0, false) return xconnectVMNetwork(endpoint, false, 0, false)
}) })
} }
// HotAttach for the virtual endpoint uses hot plug device // HotAttach for the virtual endpoint uses hot plug device
func (endpoint *VirtualEndpoint) HotAttach(h hypervisor) error { func (endpoint *VirtualEndpoint) HotAttach(h hypervisor) error {
networkLogger().Info("Hot attaching virtual endpoint") networkLogger().Info("Hot attaching virtual endpoint")
if err := xconnectVMNetwork(&(endpoint.NetPair), true, h.hypervisorConfig().NumVCPUs, h.hypervisorConfig().DisableVhostNet); err != nil { if err := xconnectVMNetwork(endpoint, true, h.hypervisorConfig().NumVCPUs, h.hypervisorConfig().DisableVhostNet); err != nil {
networkLogger().WithError(err).Error("Error bridging virtual ep") networkLogger().WithError(err).Error("Error bridging virtual ep")
return err return err
} }
@ -280,7 +294,7 @@ func (endpoint *VirtualEndpoint) HotDetach(h hypervisor, netNsCreated bool, netN
} }
networkLogger().Info("Hot detaching virtual endpoint") networkLogger().Info("Hot detaching virtual endpoint")
if err := doNetNS(netNsPath, func(_ ns.NetNS) error { if err := doNetNS(netNsPath, func(_ ns.NetNS) error {
return xconnectVMNetwork(&(endpoint.NetPair), false, 0, h.hypervisorConfig().DisableVhostNet) return xconnectVMNetwork(endpoint, false, 0, h.hypervisorConfig().DisableVhostNet)
}); err != nil { }); err != nil {
networkLogger().WithError(err).Warn("Error un-bridging virtual ep") networkLogger().WithError(err).Warn("Error un-bridging virtual ep")
} }
@ -322,6 +336,11 @@ func (endpoint *VhostUserEndpoint) PciAddr() string {
return endpoint.PCIAddr return endpoint.PCIAddr
} }
// NetworkPair returns the network pair of the endpoint.
func (endpoint *VhostUserEndpoint) NetworkPair() *NetworkInterfacePair {
return nil
}
// Attach for vhostuser endpoint // Attach for vhostuser endpoint
func (endpoint *VhostUserEndpoint) Attach(h hypervisor) error { func (endpoint *VhostUserEndpoint) Attach(h hypervisor) error {
networkLogger().WithField("endpoint-type", "vhostuser").Info("Attaching endpoint") networkLogger().WithField("endpoint-type", "vhostuser").Info("Attaching endpoint")
@ -401,6 +420,11 @@ func (endpoint *PhysicalEndpoint) SetProperties(properties NetworkInfo) {
endpoint.EndpointProperties = properties endpoint.EndpointProperties = properties
} }
// NetworkPair returns the network pair of the endpoint.
func (endpoint *PhysicalEndpoint) NetworkPair() *NetworkInterfacePair {
return nil
}
// Attach for physical endpoint binds the physical network interface to // Attach for physical endpoint binds the physical network interface to
// vfio-pci and adds device to the hypervisor with vfio-passthrough. // vfio-pci and adds device to the hypervisor with vfio-passthrough.
func (endpoint *PhysicalEndpoint) Attach(h hypervisor) error { func (endpoint *PhysicalEndpoint) Attach(h hypervisor) error {
@ -443,6 +467,83 @@ func (endpoint *PhysicalEndpoint) HotDetach(h hypervisor, netNsCreated bool, net
return fmt.Errorf("PhysicalEndpoint does not support Hot detach") return fmt.Errorf("PhysicalEndpoint does not support Hot detach")
} }
// Macvlan
// Properties returns properties of the interface.
func (endpoint *BridgedMacvlanEndpoint) Properties() NetworkInfo {
return endpoint.EndpointProperties
}
// Name returns name of the veth interface in the network pair.
func (endpoint *BridgedMacvlanEndpoint) Name() string {
return endpoint.NetPair.VirtIface.Name
}
// HardwareAddr returns the mac address that is assigned to the tap interface
// in th network pair.
func (endpoint *BridgedMacvlanEndpoint) HardwareAddr() string {
return endpoint.NetPair.TAPIface.HardAddr
}
// Type identifies the endpoint as a virtual endpoint.
func (endpoint *BridgedMacvlanEndpoint) Type() EndpointType {
return endpoint.EndpointType
}
// SetProperties sets the properties for the endpoint.
func (endpoint *BridgedMacvlanEndpoint) SetProperties(properties NetworkInfo) {
endpoint.EndpointProperties = properties
}
// PciAddr returns the PCI address of the endpoint.
func (endpoint *BridgedMacvlanEndpoint) PciAddr() string {
return endpoint.PCIAddr
}
// NetworkPair returns the network pair of the endpoint.
func (endpoint *BridgedMacvlanEndpoint) NetworkPair() *NetworkInterfacePair {
return &endpoint.NetPair
}
// Attach for virtual endpoint bridges the network pair and adds the
// tap interface of the network pair to the hypervisor.
func (endpoint *BridgedMacvlanEndpoint) Attach(h hypervisor) error {
networkLogger().Info("Attaching macvlan endpoint")
if err := xconnectVMNetwork(endpoint, true, h.hypervisorConfig().NumVCPUs, h.hypervisorConfig().DisableVhostNet); err != nil {
networkLogger().WithError(err).Error("Error bridging virtual ep")
return err
}
return h.addDevice(endpoint, netDev)
}
// Detach for the virtual endpoint tears down the tap and bridge
// created for the veth interface.
func (endpoint *BridgedMacvlanEndpoint) Detach(netNsCreated bool, netNsPath string) error {
// The network namespace would have been deleted at this point
// if it has not been created by virtcontainers.
if !netNsCreated {
return nil
}
networkLogger().Info("Detaching virtual endpoint")
return doNetNS(netNsPath, func(_ ns.NetNS) error {
//return xconnectVMNetwork(&(endpoint.NetPair), false, 0, false, endpoint.EndpointType)
return xconnectVMNetwork(endpoint, false, 0, false)
})
}
// HotAttach for physical endpoint not supported yet
func (endpoint *BridgedMacvlanEndpoint) HotAttach(h hypervisor) error {
return fmt.Errorf("BridgedMacvlanEndpoint does not support Hot attach")
}
// HotDetach for physical endpoint not supported yet
func (endpoint *BridgedMacvlanEndpoint) HotDetach(h hypervisor, netNsCreated bool, netNsPath string) error {
return fmt.Errorf("BridgedMacvlanEndpoint does not support Hot detach")
}
// EndpointType identifies the type of the network endpoint. // EndpointType identifies the type of the network endpoint.
type EndpointType string type EndpointType string
@ -455,6 +556,9 @@ const (
// VhostUserEndpointType is the vhostuser network interface. // VhostUserEndpointType is the vhostuser network interface.
VhostUserEndpointType EndpointType = "vhost-user" VhostUserEndpointType EndpointType = "vhost-user"
// BridgedMacvlanEndpointType is macvlan network interface.
BridgedMacvlanEndpointType EndpointType = "macvlan"
) )
// Set sets an endpoint type based on the input string. // Set sets an endpoint type based on the input string.
@ -469,6 +573,9 @@ func (endpointType *EndpointType) Set(value string) error {
case "vhost-user": case "vhost-user":
*endpointType = VhostUserEndpointType *endpointType = VhostUserEndpointType
return nil return nil
case "macvlan":
*endpointType = BridgedMacvlanEndpointType
return nil
default: default:
return fmt.Errorf("Unknown endpoint type %s", value) return fmt.Errorf("Unknown endpoint type %s", value)
} }
@ -483,6 +590,8 @@ func (endpointType *EndpointType) String() string {
return string(VirtualEndpointType) return string(VirtualEndpointType)
case VhostUserEndpointType: case VhostUserEndpointType:
return string(VhostUserEndpointType) return string(VhostUserEndpointType)
case BridgedMacvlanEndpointType:
return string(BridgedMacvlanEndpointType)
default: default:
return "" return ""
} }
@ -601,6 +710,18 @@ func (n *NetworkNamespace) UnmarshalJSON(b []byte) error {
"endpoint-type": "vhostuser", "endpoint-type": "vhostuser",
}).Info("endpoint unmarshalled") }).Info("endpoint unmarshalled")
case BridgedMacvlanEndpointType:
var endpoint BridgedMacvlanEndpoint
err := json.Unmarshal(e.Data, &endpoint)
if err != nil {
return err
}
networkLogger().WithFields(logrus.Fields{
"endpoint": endpoint,
"endpoint-type": "macvlan",
}).Info("endpoint unmarshalled")
default: default:
networkLogger().WithField("endpoint-type", e.Type).Error("Ignoring unknown endpoint type") networkLogger().WithField("endpoint-type", e.Type).Error("Ignoring unknown endpoint type")
} }
@ -732,6 +853,10 @@ func getLinkByName(netHandle *netlink.Handle, name string, expectedLink netlink.
if l, ok := link.(*netlink.Macvtap); ok { if l, ok := link.(*netlink.Macvtap); ok {
return l, nil return l, nil
} }
case (&netlink.Macvlan{}).Type():
if l, ok := link.(*netlink.Macvlan); ok {
return l, nil
}
default: default:
return nil, fmt.Errorf("Unsupported link type %s", expectedLink.Type()) return nil, fmt.Errorf("Unsupported link type %s", expectedLink.Type())
} }
@ -740,7 +865,9 @@ func getLinkByName(netHandle *netlink.Handle, name string, expectedLink netlink.
} }
// The endpoint type should dictate how the connection needs to be made // The endpoint type should dictate how the connection needs to be made
func xconnectVMNetwork(netPair *NetworkInterfacePair, connect bool, numCPUs uint32, disableVhostNet bool) error { func xconnectVMNetwork(endpoint Endpoint, connect bool, numCPUs uint32, disableVhostNet bool) error {
netPair := endpoint.NetworkPair()
if netPair.NetInterworkingModel == NetXConnectDefaultModel { if netPair.NetInterworkingModel == NetXConnectDefaultModel {
netPair.NetInterworkingModel = DefaultNetInterworkingModel netPair.NetInterworkingModel = DefaultNetInterworkingModel
} }
@ -748,15 +875,15 @@ func xconnectVMNetwork(netPair *NetworkInterfacePair, connect bool, numCPUs uint
case NetXConnectBridgedModel: case NetXConnectBridgedModel:
netPair.NetInterworkingModel = NetXConnectBridgedModel netPair.NetInterworkingModel = NetXConnectBridgedModel
if connect { if connect {
return bridgeNetworkPair(netPair, numCPUs, disableVhostNet) return bridgeNetworkPair(endpoint, numCPUs, disableVhostNet)
} }
return unBridgeNetworkPair(*netPair) return unBridgeNetworkPair(endpoint)
case NetXConnectMacVtapModel: case NetXConnectMacVtapModel:
netPair.NetInterworkingModel = NetXConnectMacVtapModel netPair.NetInterworkingModel = NetXConnectMacVtapModel
if connect { if connect {
return tapNetworkPair(netPair, numCPUs, disableVhostNet) return tapNetworkPair(endpoint, numCPUs, disableVhostNet)
} }
return untapNetworkPair(*netPair) return untapNetworkPair(endpoint)
case NetXConnectEnlightenedModel: case NetXConnectEnlightenedModel:
return fmt.Errorf("Unsupported networking model") return fmt.Errorf("Unsupported networking model")
default: default:
@ -843,18 +970,33 @@ func setIPs(link netlink.Link, addrs []netlink.Addr) error {
return nil return nil
} }
func tapNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVhostNet bool) error { func tapNetworkPair(endpoint Endpoint, numCPUs uint32, disableVhostNet bool) error {
netHandle, err := netlink.NewHandle() netHandle, err := netlink.NewHandle()
if err != nil { if err != nil {
return err return err
} }
defer netHandle.Delete() defer netHandle.Delete()
vethLink, err := getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Veth{}) var link netlink.Link
if err != nil { netPair := endpoint.NetworkPair()
return fmt.Errorf("Could not get veth interface: %s: %s", netPair.VirtIface.Name, err)
switch ep := endpoint.(type) {
case *VirtualEndpoint:
link, err = getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Veth{})
if err != nil {
return fmt.Errorf("Could not get interface %s : %s", netPair.VirtIface.Name, err)
}
case *BridgedMacvlanEndpoint:
link, err = getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Macvlan{})
if err != nil {
return fmt.Errorf("Could not get interface %s : %s", netPair.VirtIface.Name, err)
}
default:
return fmt.Errorf("Unexpected endpointType %s", ep.Type())
} }
vethLinkAttrs := vethLink.Attrs()
attrs := link.Attrs()
// Attach the macvtap interface to the underlying container // Attach the macvtap interface to the underlying container
// interface. Also picks relevant attributes from the parent // interface. Also picks relevant attributes from the parent
@ -862,8 +1004,8 @@ func tapNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVhostN
&netlink.Macvtap{ &netlink.Macvtap{
Macvlan: netlink.Macvlan{ Macvlan: netlink.Macvlan{
LinkAttrs: netlink.LinkAttrs{ LinkAttrs: netlink.LinkAttrs{
TxQLen: vethLinkAttrs.TxQLen, TxQLen: attrs.TxQLen,
ParentIndex: vethLinkAttrs.Index, ParentIndex: attrs.Index,
}, },
}, },
}, int(numCPUs)) }, int(numCPUs))
@ -877,18 +1019,18 @@ func tapNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVhostN
// the one inside the VM in order to avoid any firewall issues. The // the one inside the VM in order to avoid any firewall issues. The
// bridge created by the network plugin on the host actually expects // bridge created by the network plugin on the host actually expects
// to see traffic from this MAC address and not another one. // to see traffic from this MAC address and not another one.
tapHardAddr := vethLinkAttrs.HardwareAddr tapHardAddr := attrs.HardwareAddr
netPair.TAPIface.HardAddr = vethLinkAttrs.HardwareAddr.String() netPair.TAPIface.HardAddr = attrs.HardwareAddr.String()
if err := netHandle.LinkSetMTU(tapLink, vethLinkAttrs.MTU); err != nil { if err := netHandle.LinkSetMTU(tapLink, attrs.MTU); err != nil {
return fmt.Errorf("Could not set TAP MTU %d: %s", vethLinkAttrs.MTU, err) return fmt.Errorf("Could not set TAP MTU %d: %s", attrs.MTU, err)
} }
hardAddr, err := net.ParseMAC(netPair.VirtIface.HardAddr) hardAddr, err := net.ParseMAC(netPair.VirtIface.HardAddr)
if err != nil { if err != nil {
return err return err
} }
if err := netHandle.LinkSetHardwareAddr(vethLink, hardAddr); err != nil { if err := netHandle.LinkSetHardwareAddr(link, hardAddr); err != nil {
return fmt.Errorf("Could not set MAC address %s for veth interface %s: %s", return fmt.Errorf("Could not set MAC address %s for veth interface %s: %s",
netPair.VirtIface.HardAddr, netPair.VirtIface.Name, err) netPair.VirtIface.HardAddr, netPair.VirtIface.Name, err)
} }
@ -903,16 +1045,16 @@ func tapNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVhostN
} }
// Clear the IP addresses from the veth interface to prevent ARP conflict // Clear the IP addresses from the veth interface to prevent ARP conflict
netPair.VirtIface.Addrs, err = netlink.AddrList(vethLink, netlink.FAMILY_V4) netPair.VirtIface.Addrs, err = netlink.AddrList(link, netlink.FAMILY_V4)
if err != nil { if err != nil {
return fmt.Errorf("Unable to obtain veth IP addresses: %s", err) return fmt.Errorf("Unable to obtain veth IP addresses: %s", err)
} }
if err := clearIPs(vethLink, netPair.VirtIface.Addrs); err != nil { if err := clearIPs(link, netPair.VirtIface.Addrs); err != nil {
return fmt.Errorf("Unable to clear veth IP addresses: %s", err) return fmt.Errorf("Unable to clear veth IP addresses: %s", err)
} }
if err := netHandle.LinkSetUp(vethLink); err != nil { if err := netHandle.LinkSetUp(link); err != nil {
return fmt.Errorf("Could not enable veth %s: %s", netPair.VirtIface.Name, err) return fmt.Errorf("Could not enable veth %s: %s", netPair.VirtIface.Name, err)
} }
@ -934,13 +1076,15 @@ func tapNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVhostN
return nil return nil
} }
func bridgeNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVhostNet bool) error { func bridgeNetworkPair(endpoint Endpoint, numCPUs uint32, disableVhostNet bool) error {
netHandle, err := netlink.NewHandle() netHandle, err := netlink.NewHandle()
if err != nil { if err != nil {
return err return err
} }
defer netHandle.Delete() defer netHandle.Delete()
netPair := endpoint.NetworkPair()
tapLink, fds, err := createLink(netHandle, netPair.TAPIface.Name, &netlink.Tuntap{}, int(numCPUs)) tapLink, fds, err := createLink(netHandle, netPair.TAPIface.Name, &netlink.Tuntap{}, int(numCPUs))
if err != nil { if err != nil {
return fmt.Errorf("Could not create TAP interface: %s", err) return fmt.Errorf("Could not create TAP interface: %s", err)
@ -955,29 +1099,43 @@ func bridgeNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVho
netPair.VhostFds = vhostFds netPair.VhostFds = vhostFds
} }
vethLink, err := getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Veth{}) var attrs *netlink.LinkAttrs
if err != nil { var link netlink.Link
return fmt.Errorf("Could not get veth interface %s : %s", netPair.VirtIface.Name, err)
switch ep := endpoint.(type) {
case *VirtualEndpoint:
link, err = getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Veth{})
if err != nil {
return fmt.Errorf("Could not get interface %s : %s", netPair.VirtIface.Name, err)
}
case *BridgedMacvlanEndpoint:
link, err = getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Macvlan{})
if err != nil {
return fmt.Errorf("Could not get interface %s : %s", netPair.VirtIface.Name, err)
}
default:
return fmt.Errorf("Unexpected endpointType %s", ep.Type())
} }
vethLinkAttrs := vethLink.Attrs() attrs = link.Attrs()
// Save the veth MAC address to the TAP so that it can later be used // Save the veth MAC address to the TAP so that it can later be used
// to build the hypervisor command line. This MAC address has to be // to build the hypervisor command line. This MAC address has to be
// the one inside the VM in order to avoid any firewall issues. The // the one inside the VM in order to avoid any firewall issues. The
// bridge created by the network plugin on the host actually expects // bridge created by the network plugin on the host actually expects
// to see traffic from this MAC address and not another one. // to see traffic from this MAC address and not another one.
netPair.TAPIface.HardAddr = vethLinkAttrs.HardwareAddr.String() netPair.TAPIface.HardAddr = attrs.HardwareAddr.String()
if err := netHandle.LinkSetMTU(tapLink, vethLinkAttrs.MTU); err != nil { if err := netHandle.LinkSetMTU(tapLink, attrs.MTU); err != nil {
return fmt.Errorf("Could not set TAP MTU %d: %s", vethLinkAttrs.MTU, err) return fmt.Errorf("Could not set TAP MTU %d: %s", attrs.MTU, err)
} }
hardAddr, err := net.ParseMAC(netPair.VirtIface.HardAddr) hardAddr, err := net.ParseMAC(netPair.VirtIface.HardAddr)
if err != nil { if err != nil {
return err return err
} }
if err := netHandle.LinkSetHardwareAddr(vethLink, hardAddr); err != nil { if err := netHandle.LinkSetHardwareAddr(link, hardAddr); err != nil {
return fmt.Errorf("Could not set MAC address %s for veth interface %s: %s", return fmt.Errorf("Could not set MAC address %s for veth interface %s: %s",
netPair.VirtIface.HardAddr, netPair.VirtIface.Name, err) netPair.VirtIface.HardAddr, netPair.VirtIface.Name, err)
} }
@ -997,12 +1155,12 @@ func bridgeNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVho
return fmt.Errorf("Could not enable TAP %s: %s", netPair.TAPIface.Name, err) return fmt.Errorf("Could not enable TAP %s: %s", netPair.TAPIface.Name, err)
} }
if err := netHandle.LinkSetMaster(vethLink, bridgeLink.(*netlink.Bridge)); err != nil { if err := netHandle.LinkSetMaster(link, bridgeLink.(*netlink.Bridge)); err != nil {
return fmt.Errorf("Could not attach veth %s to the bridge %s: %s", return fmt.Errorf("Could not attach veth %s to the bridge %s: %s",
netPair.VirtIface.Name, netPair.Name, err) netPair.VirtIface.Name, netPair.Name, err)
} }
if err := netHandle.LinkSetUp(vethLink); err != nil { if err := netHandle.LinkSetUp(link); err != nil {
return fmt.Errorf("Could not enable veth %s: %s", netPair.VirtIface.Name, err) return fmt.Errorf("Could not enable veth %s: %s", netPair.VirtIface.Name, err)
} }
@ -1013,13 +1171,15 @@ func bridgeNetworkPair(netPair *NetworkInterfacePair, numCPUs uint32, disableVho
return nil return nil
} }
func untapNetworkPair(netPair NetworkInterfacePair) error { func untapNetworkPair(endpoint Endpoint) error {
netHandle, err := netlink.NewHandle() netHandle, err := netlink.NewHandle()
if err != nil { if err != nil {
return err return err
} }
defer netHandle.Delete() defer netHandle.Delete()
netPair := endpoint.NetworkPair()
tapLink, err := getLinkByName(netHandle, netPair.TAPIface.Name, &netlink.Macvtap{}) tapLink, err := getLinkByName(netHandle, netPair.TAPIface.Name, &netlink.Macvtap{})
if err != nil { if err != nil {
return fmt.Errorf("Could not get TAP interface %s: %s", netPair.TAPIface.Name, err) return fmt.Errorf("Could not get TAP interface %s: %s", netPair.TAPIface.Name, err)
@ -1029,28 +1189,42 @@ func untapNetworkPair(netPair NetworkInterfacePair) error {
return fmt.Errorf("Could not remove TAP %s: %s", netPair.TAPIface.Name, err) return fmt.Errorf("Could not remove TAP %s: %s", netPair.TAPIface.Name, err)
} }
vethLink, err := getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Veth{}) var link netlink.Link
if err != nil {
// The veth pair is not totally managed by virtcontainers switch ep := endpoint.(type) {
networkLogger().WithError(err).WithField("veth-name", netPair.VirtIface.Name).Warn("Could not get veth interface") case *VirtualEndpoint:
} else { link, err = getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Veth{})
if err := netHandle.LinkSetDown(vethLink); err != nil { if err != nil {
return fmt.Errorf("Could not disable veth %s: %s", netPair.VirtIface.Name, err) return fmt.Errorf("Could not get interface %s : %s", netPair.VirtIface.Name, err)
} }
case *BridgedMacvlanEndpoint:
link, err = getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Macvlan{})
if err != nil {
return fmt.Errorf("Could not get interface %s : %s", netPair.VirtIface.Name, err)
}
default:
return fmt.Errorf("Unexpected endpointType %s", ep.Type())
}
if err := netHandle.LinkSetDown(link); err != nil {
return fmt.Errorf("Could not disable veth %s: %s", netPair.VirtIface.Name, err)
} }
// Restore the IPs that were cleared // Restore the IPs that were cleared
err = setIPs(vethLink, netPair.VirtIface.Addrs) err = setIPs(link, netPair.VirtIface.Addrs)
return err return err
} }
func unBridgeNetworkPair(netPair NetworkInterfacePair) error { func unBridgeNetworkPair(endpoint Endpoint) error {
netHandle, err := netlink.NewHandle() netHandle, err := netlink.NewHandle()
if err != nil { if err != nil {
return err return err
} }
defer netHandle.Delete() defer netHandle.Delete()
netPair := endpoint.NetworkPair()
tapLink, err := getLinkByName(netHandle, netPair.TAPIface.Name, &netlink.Tuntap{}) tapLink, err := getLinkByName(netHandle, netPair.TAPIface.Name, &netlink.Tuntap{})
if err != nil { if err != nil {
return fmt.Errorf("Could not get TAP interface: %s", err) return fmt.Errorf("Could not get TAP interface: %s", err)
@ -1081,19 +1255,30 @@ func unBridgeNetworkPair(netPair NetworkInterfacePair) error {
return fmt.Errorf("Could not remove TAP %s: %s", netPair.TAPIface.Name, err) return fmt.Errorf("Could not remove TAP %s: %s", netPair.TAPIface.Name, err)
} }
vethLink, err := getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Veth{}) var link netlink.Link
if err != nil {
// The veth pair is not totally managed by virtcontainers switch ep := endpoint.(type) {
networkLogger().WithError(err).Warn("Could not get veth interface") case *VirtualEndpoint:
} else { link, err = getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Veth{})
if err := netHandle.LinkSetDown(vethLink); err != nil { if err != nil {
return fmt.Errorf("Could not disable veth %s: %s", netPair.VirtIface.Name, err) return fmt.Errorf("Could not get interface %s : %s", netPair.VirtIface.Name, err)
}
case *BridgedMacvlanEndpoint:
link, err = getLinkByName(netHandle, netPair.VirtIface.Name, &netlink.Macvlan{})
if err != nil {
return fmt.Errorf("Could not get interface %s : %s", netPair.VirtIface.Name, err)
} }
if err := netHandle.LinkSetNoMaster(vethLink); err != nil { default:
return fmt.Errorf("Could not detach veth %s: %s", netPair.VirtIface.Name, err) return fmt.Errorf("Unexpected endpointType %s", ep.Type())
} }
if err := netHandle.LinkSetDown(link); err != nil {
return fmt.Errorf("Could not disable veth %s: %s", netPair.VirtIface.Name, err)
}
if err := netHandle.LinkSetNoMaster(link); err != nil {
return fmt.Errorf("Could not detach veth %s: %s", netPair.VirtIface.Name, err)
} }
return nil return nil
@ -1160,29 +1345,33 @@ func createVirtualNetworkEndpoint(idx int, ifName string, interworkingModel NetI
return &VirtualEndpoint{}, fmt.Errorf("invalid network endpoint index: %d", idx) return &VirtualEndpoint{}, fmt.Errorf("invalid network endpoint index: %d", idx)
} }
uniqueID := uuid.Generate().String() netPair := createNetworkInterfacePair(idx, ifName, interworkingModel)
hardAddr := net.HardwareAddr{0x02, 0x00, 0xCA, 0xFE, byte(idx >> 8), byte(idx)}
endpoint := &VirtualEndpoint{ endpoint := &VirtualEndpoint{
// TODO This is too specific. We may need to create multiple // TODO This is too specific. We may need to create multiple
// end point types here and then decide how to connect them // end point types here and then decide how to connect them
// at the time of hypervisor attach and not here // at the time of hypervisor attach and not here
NetPair: NetworkInterfacePair{ NetPair: netPair,
ID: uniqueID,
Name: fmt.Sprintf("br%d_kata", idx),
VirtIface: NetworkInterface{
Name: fmt.Sprintf("eth%d", idx),
HardAddr: hardAddr.String(),
},
TAPIface: NetworkInterface{
Name: fmt.Sprintf("tap%d_kata", idx),
},
NetInterworkingModel: interworkingModel,
},
EndpointType: VirtualEndpointType, EndpointType: VirtualEndpointType,
} }
if ifName != "" {
endpoint.NetPair.VirtIface.Name = ifName
}
return endpoint, nil
}
func createBridgedMacvlanNetworkEndpoint(idx int, ifName string, interworkingModel NetInterworkingModel) (*BridgedMacvlanEndpoint, error) {
if idx < 0 {
return &BridgedMacvlanEndpoint{}, fmt.Errorf("invalid network endpoint index: %d", idx)
}
netPair := createNetworkInterfacePair(idx, ifName, interworkingModel)
endpoint := &BridgedMacvlanEndpoint{
NetPair: netPair,
EndpointType: BridgedMacvlanEndpointType,
}
if ifName != "" { if ifName != "" {
endpoint.NetPair.VirtIface.Name = ifName endpoint.NetPair.VirtIface.Name = ifName
} }
@ -1277,6 +1466,28 @@ func generateInterfacesAndRoutes(networkNS NetworkNamespace) ([]*grpc.Interface,
return ifaces, routes, nil return ifaces, routes, nil
} }
func createNetworkInterfacePair(idx int, ifName string, interworkingModel NetInterworkingModel) NetworkInterfacePair {
uniqueID := uuid.Generate().String()
hardAddr := net.HardwareAddr{0x02, 0x00, 0xCA, 0xFE, byte(idx >> 8), byte(idx)}
netPair := NetworkInterfacePair{
ID: uniqueID,
Name: fmt.Sprintf("br%d_kata", idx),
VirtIface: NetworkInterface{
Name: fmt.Sprintf("eth%d", idx),
HardAddr: hardAddr.String(),
},
TAPIface: NetworkInterface{
Name: fmt.Sprintf("tap%d_kata", idx),
},
NetInterworkingModel: interworkingModel,
}
return netPair
}
func networkInfoFromLink(handle *netlink.Handle, link netlink.Link) (NetworkInfo, error) { func networkInfoFromLink(handle *netlink.Handle, link netlink.Link) (NetworkInfo, error) {
addrs, err := handle.AddrList(link, netlink.FAMILY_ALL) addrs, err := handle.AddrList(link, netlink.FAMILY_ALL)
if err != nil { if err != nil {
@ -1388,6 +1599,9 @@ func createEndpoint(netInfo NetworkInfo, idx int, model NetInterworkingModel) (E
if socketPath != "" { if socketPath != "" {
networkLogger().WithField("interface", netInfo.Iface.Name).Info("VhostUser network interface found") networkLogger().WithField("interface", netInfo.Iface.Name).Info("VhostUser network interface found")
endpoint, err = createVhostUserEndpoint(netInfo, socketPath) endpoint, err = createVhostUserEndpoint(netInfo, socketPath)
} else if netInfo.Iface.Type == "macvlan" {
networkLogger().Infof("macvlan interface found")
endpoint, err = createBridgedMacvlanNetworkEndpoint(idx, netInfo.Iface.Name, model)
} else { } else {
endpoint, err = createVirtualNetworkEndpoint(idx, netInfo.Iface.Name, model) endpoint, err = createVirtualNetworkEndpoint(idx, netInfo.Iface.Name, model)
} }

View File

@ -441,20 +441,21 @@ func networkModelToQemuType(model NetInterworkingModel) govmmQemu.NetDeviceType
func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoint) []govmmQemu.Device { func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoint) []govmmQemu.Device {
switch ep := endpoint.(type) { switch ep := endpoint.(type) {
case *VirtualEndpoint: case *VirtualEndpoint, *BridgedMacvlanEndpoint:
netPair := ep.NetworkPair()
devices = append(devices, devices = append(devices,
govmmQemu.NetDevice{ govmmQemu.NetDevice{
Type: networkModelToQemuType(ep.NetPair.NetInterworkingModel), Type: networkModelToQemuType(netPair.NetInterworkingModel),
Driver: govmmQemu.VirtioNetPCI, Driver: govmmQemu.VirtioNetPCI,
ID: fmt.Sprintf("network-%d", q.networkIndex), ID: fmt.Sprintf("network-%d", q.networkIndex),
IFName: ep.NetPair.TAPIface.Name, IFName: netPair.TAPIface.Name,
MACAddress: ep.NetPair.TAPIface.HardAddr, MACAddress: netPair.TAPIface.HardAddr,
DownScript: "no", DownScript: "no",
Script: "no", Script: "no",
VHost: q.vhost, VHost: q.vhost,
DisableModern: q.nestedRun, DisableModern: q.nestedRun,
FDs: ep.NetPair.VMFds, FDs: netPair.VMFds,
VhostFDs: ep.NetPair.VhostFds, VhostFDs: netPair.VhostFds,
}, },
) )
q.networkIndex++ q.networkIndex++