Refactor core network logic to NetworkManager

This commit is contained in:
Onur Filiz 2016-09-21 15:39:25 -07:00
Родитель 78f8d0d54b
Коммит 82c725da46
6 изменённых файлов: 761 добавлений и 1273 удалений

Просмотреть файл

@ -1,603 +0,0 @@
// Copyright Microsoft Corp.
// All rights reserved.
package core
import (
"crypto/rand"
"errors"
"fmt"
"log"
"net"
"strconv"
"strings"
"github.com/Azure/Aqua/ebtables"
"github.com/Azure/Aqua/netlink"
)
type interfaceDetails struct {
Address net.IPNet
AddressIPV6 net.IPNet
MacAddress net.HardwareAddr
ID int
SrcName string
DstPrefix string
GatewayIPv4 net.IP
}
type ca struct {
ip *net.IP
ipNet *net.IPNet
caName string
caType string
}
type enslavedInterface struct {
rnmAllocatedMacAddress net.HardwareAddr
modifiedMacAddress net.HardwareAddr
nicName string
bridgeName string
provisionedCas map[string]ca
}
type vethPair struct {
used bool
peer1 int
peer2 int
ifaceNameCaWasTakenFrom string
ip *net.IP
ipNet *net.IPNet
}
var mapEnslavedInterfaces map[string]enslavedInterface
var vethPairCollection map[int]vethPair
var vethPrefix = "azveth"
// GetInterfaceToAttach is a function that contains the logic to create/select
// the interface that will be attached to the container
// It is deprecated now
func GetInterfaceToAttach(interfaceNameToAttach string, ipAddressToAttach string) (net.IPNet, net.IPNet, net.HardwareAddr, int, string, string, net.IP, string) {
fmt.Println("Request came for", ipAddressToAttach)
var selectedInterface net.Interface
selected := false
hostInterfaces, err := net.Interfaces()
if err != nil {
ermsg := "Azure: Got error while retrieving interfaces"
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, ermsg
}
fmt.Println("Azure: Going to select an interface for container")
for _, hostInterface := range hostInterfaces {
addresses, ok := hostInterface.Addrs()
flag := hostInterface.Flags & net.FlagBroadcast
loopbackFlag := hostInterface.Flags & net.FlagLoopback
canBeSelected := ok == nil &&
// interface is configured with some ip address
len(addresses) > 0 &&
// interface supports broadcast access capability
flag == net.FlagBroadcast &&
// interface is not a loopback interface
loopbackFlag != net.FlagLoopback // &&
//strings.Contains(hostInterface.Name, "veth")
if ipAddressToAttach == "" {
if canBeSelected && interfaceNameToAttach != "" {
isThisSameAsRequested := hostInterface.Name == interfaceNameToAttach
canBeSelected = canBeSelected && isThisSameAsRequested
}
if canBeSelected {
selectedInterface = hostInterface
selected = true
}
} else {
if canBeSelected {
doesThisInterfaceHaveSameIPAsRequested := false
addrs, _ := hostInterface.Addrs()
for _, addr := range addrs {
address := addr.String()
if strings.Split(address, "/")[0] == ipAddressToAttach {
doesThisInterfaceHaveSameIPAsRequested = true
break
}
}
canBeSelected = canBeSelected && doesThisInterfaceHaveSameIPAsRequested
}
if canBeSelected {
selectedInterface = hostInterface
selected = true
}
}
}
if !selected {
ermsg := "Azure: Interface Not Found Error. " +
"It is possible that none of the interfaces is configured properly, " +
"or none of configured interfaces match the selection criteria."
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, ermsg
}
fmt.Println("Selected interface: ", selectedInterface.Name)
addresses, _ := selectedInterface.Addrs()
address := addresses[0].String()
ipv4, ipv4Net, _ := net.ParseCIDR(address)
ipv4Net.IP = ipv4
bytes := strings.Split(address, ".")
gateway := bytes[0] + "." + bytes[1] + "." + bytes[2] + ".1"
gatewayIpv4 := net.ParseIP(gateway)
srcName := selectedInterface.Name
macAddress, _ := net.ParseMAC(selectedInterface.HardwareAddr.String())
fmt.Println("Azure: Interface ip/netmask: ",
ipv4Net.IP.String(), "/", ipv4Net.Mask.String())
fmt.Println("Azure: Gateway IP: ", gatewayIpv4.String())
retval := &interfaceDetails{
Address: *ipv4Net,
MacAddress: macAddress,
SrcName: srcName,
DstPrefix: srcName + "eth",
GatewayIPv4: gatewayIpv4,
}
fmt.Println("Azure: Successfully selected interface ", retval)
return *ipv4Net, net.IPNet{}, macAddress, -1, srcName, srcName + "eth", gatewayIpv4, ""
}
// CleanupAfterContainerDeletion cleans up
func CleanupAfterContainerDeletion(ifaceName string, macAddress net.HardwareAddr) error {
// ifaceName should be of the form veth followed by an even number
fmt.Println("Going to cleanup for " + ifaceName + " -- " + macAddress.String())
seq := strings.SplitAfter(ifaceName, vethPrefix)
val, err := strconv.ParseUint(seq[1], 10, 32)
if err != nil {
return err
}
fmt.Println("Got index of veth pair as " + fmt.Sprintf("%d", val))
targetVeth, ok := vethPairCollection[int(val)]
if ok {
fmt.Println("The object contains " + targetVeth.ifaceNameCaWasTakenFrom + " " + string(targetVeth.peer1))
} else {
fmt.Println("Received null target veth pair")
return errors.New("received null veth pair for cleanup")
}
netlink.DeleteLink(ifaceName)
err = ebtables.RemoveDnatBasedOnIPV4Address(targetVeth.ip.String(), macAddress.String())
if err != nil {
fmt.Println(err.Error())
return err
}
a := targetVeth.ip
fmt.Println("going to add " + a.String() + "-- to " + targetVeth.ifaceNameCaWasTakenFrom)
netlink.AddIpAddress(targetVeth.ifaceNameCaWasTakenFrom, *(targetVeth.ip), targetVeth.ipNet)
delete(vethPairCollection, int(val))
return nil
}
// GetTargetInterface returns the interface to be moved to container name space
func GetTargetInterface(interfaceNameToAttach string, ipAddressToAttach string) (
net.IPNet, net.IPNet, net.HardwareAddr, int, string, string, net.IP, string) {
targetNic, errmsg := getInterfaceWithMultipleConfiguredCAs(ipAddressToAttach)
if errmsg != "" {
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, errmsg
}
err := enslaveInterfaceIfRequired(targetNic, "aqua")
if err != nil {
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, err.Error()
}
ip, ipNet, err := getAvailableCaAndRemoveFromHostInterface(targetNic, ipAddressToAttach)
if err != nil {
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, err.Error()
}
pair, err := generateVethPair(targetNic.Name, ip, ipNet)
if err != nil {
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, err.Error()
}
name1 := fmt.Sprintf("%s%d", vethPrefix, pair.peer1)
name2 := fmt.Sprintf("%s%d", vethPrefix, pair.peer2)
fmt.Println("Received veth pair names as ", name1, "-", name2, ". Now creating these.")
err = netlink.AddVethPair(name1, name2)
if err != nil {
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, err.Error()
}
fmt.Println("Successfully generated veth pair.")
fmt.Println("Going to add ip address ", *ip, ipNet, " to ", name1)
err = netlink.AddIpAddress(name1, *ip, ipNet)
if err != nil {
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, err.Error()
}
fmt.Println("Successfully added ip address ", *ip, ipNet, " to ", name1)
fmt.Println("Updating veth pair state")
fmt.Println("Going to set ", name2, " as up.")
err = netlink.SetLinkState(name2, true)
if err != nil {
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, err.Error()
}
fmt.Println("successfully ifupped ", name2, ".")
fmt.Println("Going to add ", name2, " to aqua.")
err = netlink.SetLinkMaster(name2, "aqua")
if err != nil {
fmt.Println(err.Error())
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, err.Error()
}
fmt.Println("Selected interface for container: ", name1)
selectedInterface, _ := net.InterfaceByName(name1)
addresses, _ := selectedInterface.Addrs()
address := addresses[0].String()
ipv4, ipv4Net, _ := net.ParseCIDR(address)
ipv4Net.IP = ipv4
bytes := strings.Split(address, ".")
// bug: this needs fixing
gateway := bytes[0] + "." + bytes[1] + "." + bytes[2] + ".1"
gatewayIpv4 := net.ParseIP(gateway)
macAddress, _ := net.ParseMAC(selectedInterface.HardwareAddr.String())
err = ebtables.SetupDnatBasedOnIPV4Address(ipv4.String(), macAddress.String())
if err != nil {
return net.IPNet{}, net.IPNet{}, nil, -1, "", "", net.IP{}, err.Error()
}
fmt.Println("Azure: Interface ip/netmask: ",
ipv4Net.IP.String(), "/", ipv4Net.Mask.String())
fmt.Println("Azure: Gateway IP: ", gatewayIpv4.String())
return *ipv4Net, net.IPNet{}, macAddress, -1, name1, name1 + "eth", gatewayIpv4, ""
}
// FreeSlaves will free slaves and cleans up stuff
func FreeSlaves() error {
for ifaceName, ifaceDetails := range mapEnslavedInterfaces {
fmt.Println("Going to remove " + ifaceName + " from bridge")
err := netlink.SetLinkMaster(ifaceName, "")
fmt.Println("Going to if down the interface so that mac address can be fixed")
err = netlink.SetLinkState(ifaceName, false)
if err != nil {
return err
}
macAddress := ifaceDetails.rnmAllocatedMacAddress
fmt.Println("Going to revert hardware address of " + ifaceName + " to " + macAddress.String())
err = netlink.SetLinkAddress(ifaceName, macAddress)
if err != nil {
return err
}
fmt.Println("Going to if up")
err = netlink.SetLinkState(ifaceName, true)
if err != nil {
return err
}
// cleanup dnat for arp and snat for outgoing
fmt.Println("Going to clean up dnat for arp replies")
ebtables.CleanupDnatForArpReplies(ifaceName)
fmt.Println("Going to clean up snat for outgoing packets")
ebtables.CleanupSnatForOutgoingPackets(ifaceName, macAddress.String())
fmt.Println("Clean up finished...")
fmt.Println("Going to add ip addresses back to interface " + ifaceName)
for _, caDetails := range ifaceDetails.provisionedCas {
netlink.AddIpAddress(ifaceName, *(caDetails.ip), caDetails.ipNet)
}
}
return nil
}
func generateVethPair(ifaceNameCaWasTakenFrom string, ip *net.IP, ipNet *net.IPNet) (vethPair, error) {
var vethpair vethPair
fmt.Println("Going to generate veth pair names")
if vethPairCollection == nil {
vethPairCollection = make(map[int]vethPair)
vethpair.used = true
vethpair.peer1 = 0
vethpair.peer2 = 1
vethpair.ifaceNameCaWasTakenFrom = ifaceNameCaWasTakenFrom
vethpair.ip = ip
vethpair.ipNet = ipNet
vethPairCollection[0] = vethpair
return vethpair, nil
}
for i := uint32(0); i < ^uint32(0); i += 2 {
_, ok := vethPairCollection[int(i)]
if !ok {
vethpair.used = true
vethpair.peer1 = int(i)
vethpair.peer2 = int(i + 1)
vethpair.ifaceNameCaWasTakenFrom = ifaceNameCaWasTakenFrom
vethpair.ip = ip
vethpair.ipNet = ipNet
vethPairCollection[int(i)] = vethpair
return vethpair, nil
}
}
fmt.Println("Unable to generate veth pair")
return vethpair, errors.New("unable to generate veth pair")
}
func getInterfaceWithMultipleConfiguredCAs(ipAddressToAttach string) (*net.Interface, string) {
var selectedInterface net.Interface
selected := false
hostInterfaces, err := net.Interfaces()
if err != nil {
ermsg := "Azure: Got error while retrieving interfaces."
return nil, ermsg
}
fmt.Println("Azure: Going to select an interface that has multiple CAs.")
for _, hostInterface := range hostInterfaces {
// We need to get ip addresses that are available to be used from this
// interface.
// We can get CAs provisioned on this interface from metadata server.
// The current allocation either lives in memory, or we go through current
// containers and see what has already been allocated.
// For now, we assume that all CAs are configured on the interface.
// Whenever our driver picks up a CA, it removes it from the interface.
// When container is destriyed, we add CA back on the interface.
// So our state lives on the interface and in docker containers.
addresses, ok := hostInterface.Addrs()
flag := hostInterface.Flags & net.FlagBroadcast
loopbackFlag := hostInterface.Flags & net.FlagLoopback
canBeSelected := ok == nil &&
// interface is configured with some ip address
len(addresses) > 1 && // only multi CA interfaces for now
// interface supports broadcast access capability
flag == net.FlagBroadcast &&
// interface is not a loopback interface
loopbackFlag != net.FlagLoopback &&
// temporary hack until we have metadata server
!strings.Contains(hostInterface.Name, "veth") &&
!strings.Contains(hostInterface.Name, "vth") &&
!strings.Contains(hostInterface.Name, "eth0") &&
!strings.Contains(hostInterface.Name, "docker")
if ipAddressToAttach == "" {
if canBeSelected {
selectedInterface = hostInterface
selected = true
}
} else {
if canBeSelected {
doesThisInterfaceHaveSameIPAsRequested := false
addrs, _ := hostInterface.Addrs()
for _, addr := range addrs {
address := addr.String()
if strings.Split(address, "/")[0] == ipAddressToAttach {
doesThisInterfaceHaveSameIPAsRequested = true
break
}
}
canBeSelected = canBeSelected && doesThisInterfaceHaveSameIPAsRequested
}
if canBeSelected {
selectedInterface = hostInterface
selected = true
}
}
if canBeSelected {
selectedInterface = hostInterface
selected = true
}
}
// it may be already enslaved
// interface loose cas once it is added to bridge
if !selected && mapEnslavedInterfaces != nil {
fmt.Println("Was unable to find an interface that is not already slave")
for ifaceName, ifaceDetails := range mapEnslavedInterfaces {
fmt.Println("Searching through " + ifaceName)
for caName, caDetails := range ifaceDetails.provisionedCas {
fmt.Println("Going to check " + caName + " " + caDetails.ip.String())
if !isCaAlreadyAssigned(caName, caDetails) {
ipAddress := caDetails.ip.String()
fmt.Println("going to compare " + ipAddress + " with " + ipAddressToAttach)
if ipAddress == ipAddressToAttach || ipAddressToAttach == "" {
iface, err := net.InterfaceByName(ifaceName)
if err == nil {
selectedInterface = *iface
selected = true
break
} else {
fmt.Println(err.Error())
}
}
} else {
fmt.Println("Already assigned " + caName + " " + caDetails.ip.String())
}
}
}
}
if !selected {
ermsg := "Azure: Interface Not Found Error. " +
"It is possible that none of the interfaces is configured properly, " +
"or none of configured interfaces match the selection criteria."
fmt.Println(ermsg)
return nil, ermsg
}
fmt.Println("Azure: Successfully selected interface ", selectedInterface)
return &selectedInterface, ""
}
func isCaAlreadyAssigned(caName string, caDetails ca) bool {
for _, pairDetails := range vethPairCollection {
ipAddress := pairDetails.ip.String()
if ipAddress == caDetails.ip.String() {
return true
}
}
return false
}
func enslaveInterfaceIfRequired(iface *net.Interface, bridge string) error {
if mapEnslavedInterfaces == nil {
mapEnslavedInterfaces = make(map[string]enslavedInterface)
}
if _, ok := mapEnslavedInterfaces[iface.Name]; ok {
// already enslaved
return nil
}
_, err := net.InterfaceByName(bridge)
if err != nil {
// bridge does not exist
if err := netlink.AddLink(bridge, "bridge"); err != nil {
return err
}
}
fmt.Println("Going to iff up the bridge " + bridge)
err = netlink.SetLinkState(bridge, true)
if err != nil {
return err
}
fmt.Println("Going to SetupSnatForOutgoingPackets " + iface.Name + " " + iface.HardwareAddr.String())
err = ebtables.SetupSnatForOutgoingPackets(iface.Name, iface.HardwareAddr.String())
if err != nil {
return err
}
fmt.Println("Going to SetupDnatForArpReplies")
err = ebtables.SetupDnatForArpReplies(iface.Name)
if err != nil {
return err
}
fmt.Println("Going to iff down " + iface.Name)
err = netlink.SetLinkState(iface.Name, false)
if err != nil {
fmt.Println(err.Error())
return err
}
newMac, err := generateHardwareAddress()
if err != nil {
fmt.Println("Error happenned while generating a new mac address " + err.Error())
return err
}
fmt.Println("Generated hardware address as " + newMac.String())
var slave enslavedInterface
slave.nicName = iface.Name
slave.bridgeName = bridge
slave.rnmAllocatedMacAddress = iface.HardwareAddr
slave.modifiedMacAddress = newMac
slave.provisionedCas = make(map[string]ca)
addrs, _ := iface.Addrs()
for _, addr := range addrs {
ipl, ipNetl, _ := net.ParseCIDR(addr.String())
var caDetails ca
caDetails.ip = &ipl
caDetails.ipNet = ipNetl
slave.provisionedCas[ipl.String()] = caDetails
fmt.Println("Adding provisioned CA " + caDetails.ip.String() + " for " + iface.Name)
}
fmt.Println("Going to set " + newMac.String() + " on " + iface.Name)
err = netlink.SetLinkAddress(iface.Name, newMac)
if err != nil {
fmt.Println(err.Error())
return err
}
fmt.Println("Going to iff up the link " + iface.Name)
err = netlink.SetLinkState(iface.Name, true)
if err != nil {
fmt.Println(err.Error())
return err
}
fmt.Println("Going to add link " + iface.Name + " to " + bridge)
err = netlink.SetLinkMaster(iface.Name, bridge)
if err != nil {
fmt.Println(err.Error())
return err
}
mapEnslavedInterfaces[iface.Name] = slave
return nil
}
// From Go playground: http://play.golang.org/p/1eND0es4Nf
func generateHardwareAddress() (net.HardwareAddr, error) {
buf := make([]byte, 6)
_, err := rand.Read(buf)
if err != nil {
fmt.Println("error:", err)
return nil, err
}
// Set the local bit
buf[0] &= 2
macInString := fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5])
fmt.Println("Generated mac as " + macInString)
hwAddr, err := net.ParseMAC(macInString)
if err != nil {
return nil, err
}
return hwAddr, nil
}
func getAvailableCaAndRemoveFromHostInterface(iface *net.Interface, ipAddressToAttach string) (*net.IP, *net.IPNet, error) {
ensalvedIface, found := mapEnslavedInterfaces[iface.Name]
if !found {
erMsg := fmt.Sprintf("The interface %s was never enslaved (not possible). returning error", iface.Name)
log.Printf(fmt.Sprintf("getAvailableCaAndRemoveFromHostInterface %s", erMsg))
return nil, nil, errors.New(erMsg)
}
if ipAddressToAttach != "" {
targetCa, found := ensalvedIface.provisionedCas[ipAddressToAttach]
if !found {
erMsg := "Azure Critical Core: requested CA not found on interface " + ipAddressToAttach
fmt.Println(erMsg)
return nil, nil, errors.New(erMsg)
}
if isCaAlreadyAssigned(targetCa.caName, targetCa) {
erMsg := "Azure Critical Core: requested CA found but already in use with a container " + ipAddressToAttach
fmt.Println(erMsg)
return nil, nil, errors.New(erMsg)
}
netlink.DeleteIpAddress(iface.Name, *targetCa.ip, targetCa.ipNet)
return targetCa.ip, targetCa.ipNet, nil
}
// if ip address is not requested, then use any ca that is unused
// we have no way to tell what is primary right now so we cannot avoid removing primary
for caName, caDetails := range ensalvedIface.provisionedCas {
if !isCaAlreadyAssigned(caName, caDetails) {
fmt.Println("Found an unused CA " + caName)
netlink.DeleteIpAddress(iface.Name, *caDetails.ip, caDetails.ipNet)
return caDetails.ip, caDetails.ipNet, nil
}
}
erMsg := "Azure Critical Core: no unused ca found on interface " + iface.Name
fmt.Println(erMsg)
return nil, nil, errors.New(erMsg)
}

Просмотреть файл

@ -1,475 +0,0 @@
// Copyright Microsoft Corp.
// All rights reserved.
package core
import (
"fmt"
"net"
"sync"
"time"
"github.com/Azure/Aqua/ebtables"
"github.com/Azure/Aqua/log"
"github.com/Azure/Aqua/netlink"
"golang.org/x/sys/unix"
)
const (
// Prefix for bridge names.
bridgePrefix = "aqua"
// Prefix for host virtual network interface names.
hostInterfacePrefix = "veth"
// Prefix for container network interface names.
containerInterfacePrefix = "eth"
)
// Network represents a container network.
type Network struct {
Id string
ExtIf *externalInterface
Endpoints map[string]*Endpoint
}
// Endpoint represents a container network interface.
type Endpoint struct {
IPv4Address net.IPNet
IPv6Address net.IPNet
MacAddress net.HardwareAddr
SrcName string
DstPrefix string
IPv4Gateway net.IP
IPv6Gateway net.IP
}
// ExternalInterface represents a host network interface that bridges containers to external networks.
type externalInterface struct {
name string
macAddress net.HardwareAddr
ipAddresses []*net.IPNet
routes []*netlink.Route
ipv4Gateway net.IP
ipv6Gateway net.IP
subnets []string
bridgeName string
networks map[string]*Network
}
// Core network state.
var state struct {
externalInterfaces map[string]*externalInterface
sync.Mutex
}
// Initializes core network module.
func init() {
// Log platform information to help with debugging.
logPlatformInfo()
logNetworkInterfaces()
state.externalInterfaces = make(map[string]*externalInterface)
}
// Adds an interface to the list of available external interfaces.
func NewExternalInterface(ifName string, subnet string) error {
state.Lock()
defer state.Unlock()
// Check whether the external interface is already configured.
if state.externalInterfaces[ifName] != nil {
return nil
}
// Find the host interface.
hostIf, err := net.InterfaceByName(ifName)
if err != nil {
return err
}
extIf := externalInterface{
name: ifName,
macAddress: hostIf.HardwareAddr,
ipv4Gateway: net.IPv4zero,
ipv6Gateway: net.IPv6unspecified,
}
extIf.subnets = append(extIf.subnets, subnet)
extIf.networks = make(map[string]*Network)
state.externalInterfaces[ifName] = &extIf
log.Printf("[core] Added ExternalInterface %v for subnet %v\n", ifName, subnet)
return nil
}
// Removes an interface from the list of available external interfaces.
func DeleteExternalInterface(ifName string) error {
state.Lock()
defer state.Unlock()
delete(state.externalInterfaces, ifName)
log.Printf("[core] Removed ExternalInterface %v\n", ifName)
return nil
}
// Finds an external interface connected to the given subnet.
func findExternalInterfaceBySubnet(subnet string) *externalInterface {
for _, extIf := range state.externalInterfaces {
for _, s := range extIf.subnets {
if s == subnet {
return extIf
}
}
}
return nil
}
// Connects a host interface to a bridge.
func connectExternalInterface(extIf *externalInterface) error {
var addrs []net.Addr
log.Printf("[core] Connecting interface %v\n", extIf.name)
// Find the external interface.
hostIf, err := net.InterfaceByName(extIf.name)
if err != nil {
return err
}
// Create the bridge.
bridgeName := fmt.Sprintf("%s%d", bridgePrefix, hostIf.Index)
bridge, err := net.InterfaceByName(bridgeName)
if err != nil {
err = netlink.AddLink(bridgeName, "bridge")
if err != nil {
return err
}
bridge, err = net.InterfaceByName(bridgeName)
if err != nil {
goto cleanup
}
}
// Query the default routes on the external interface.
extIf.routes, err = netlink.GetIpRoute(&netlink.Route{Dst: &net.IPNet{}, LinkIndex: hostIf.Index})
if err != nil {
log.Printf("[core] Failed to query routes, err=%v\n", err)
goto cleanup
}
// Assign external interface's IP addresses to the bridge for host traffic.
addrs, _ = hostIf.Addrs()
for _, addr := range addrs {
ipAddr, ipNet, err := net.ParseCIDR(addr.String())
ipNet.IP = ipAddr
if err != nil {
continue
}
if !ipAddr.IsGlobalUnicast() {
continue
}
extIf.ipAddresses = append(extIf.ipAddresses, ipNet)
log.Printf("[core] Moving IP address %v to bridge %v\n", ipNet, bridgeName)
err = netlink.DeleteIpAddress(extIf.name, ipAddr, ipNet)
if err != nil {
goto cleanup
}
err = netlink.AddIpAddress(bridgeName, ipAddr, ipNet)
if err != nil {
goto cleanup
}
}
// Setup MAC address translation rules for external interface.
err = ebtables.SetupSnatForOutgoingPackets(hostIf.Name, hostIf.HardwareAddr.String())
if err != nil {
goto cleanup
}
err = ebtables.SetupDnatForArpReplies(hostIf.Name)
if err != nil {
goto cleanup
}
// External interface down.
err = netlink.SetLinkState(hostIf.Name, false)
if err != nil {
goto cleanup
}
// Connect the external interface to the bridge.
err = netlink.SetLinkMaster(hostIf.Name, bridgeName)
if err != nil {
goto cleanup
}
// External interface up.
err = netlink.SetLinkState(hostIf.Name, true)
if err != nil {
goto cleanup
}
// Bridge up.
err = netlink.SetLinkState(bridgeName, true)
if err != nil {
goto cleanup
}
// Setup routes on bridge.
for _, route := range extIf.routes {
if route.Dst == nil {
if route.Family == unix.AF_INET {
extIf.ipv4Gateway = route.Gw
} else if route.Family == unix.AF_INET6 {
extIf.ipv6Gateway = route.Gw
}
}
route.LinkIndex = bridge.Index
err = netlink.AddIpRoute(route)
route.LinkIndex = hostIf.Index
if err != nil {
log.Printf("[core] Failed to add route %+v, err=%v\n", route, err)
goto cleanup
}
log.Printf("[core] Added IP route %+v\n", route)
}
extIf.bridgeName = bridgeName
log.Printf("[core] Connected interface %v to bridge %v\n", extIf.name, extIf.bridgeName)
return nil
cleanup:
// Roll back the changes for the network.
ebtables.CleanupDnatForArpReplies(extIf.name)
ebtables.CleanupSnatForOutgoingPackets(extIf.name, extIf.macAddress.String())
netlink.DeleteLink(bridgeName)
return err
}
// Disconnects a host interface from its bridge.
func disconnectExternalInterface(extIf *externalInterface) error {
log.Printf("[core] Disconnecting interface %v\n", extIf.name)
// Cleanup MAC address translation rules.
ebtables.CleanupDnatForArpReplies(extIf.name)
ebtables.CleanupSnatForOutgoingPackets(extIf.name, extIf.macAddress.String())
// Disconnect external interface from its bridge.
err := netlink.SetLinkMaster(extIf.name, "")
if err != nil {
log.Printf("[core] Failed to disconnect interface %v from bridge, err=%v\n", extIf.name, err)
}
// Delete the bridge.
err = netlink.DeleteLink(extIf.bridgeName)
if err != nil {
log.Printf("[core] Failed to delete bridge %v, err=%v\n", extIf.bridgeName, err)
}
extIf.bridgeName = ""
// Restore IP addresses.
for _, addr := range extIf.ipAddresses {
log.Printf("[core] Moving IP address %v to interface %v\n", addr, extIf.name)
err = netlink.AddIpAddress(extIf.name, addr.IP, addr)
if err != nil {
log.Printf("[core] Failed to add IP address %v, err=%v\n", addr, err)
}
}
extIf.ipAddresses = nil
// Restore routes.
for _, route := range extIf.routes {
log.Printf("[core] Adding IP route %v to interface %v\n", route, extIf.name)
err = netlink.AddIpRoute(route)
if err != nil {
log.Printf("[core] Failed to add IP route %v, err=%v\n", route, err)
}
}
extIf.routes = nil
log.Printf("[core] Disconnected interface %v\n", extIf.name)
return nil
}
// Restarts an interface by setting its operational state down and back up.
func restartInterface(ifName string) error {
err := netlink.SetLinkState(ifName, false)
if err != nil {
return err
}
// Delay for the state to settle.
time.Sleep(2 * time.Second)
err = netlink.SetLinkState(ifName, true)
return err
}
// Creates a container network.
func CreateNetwork(networkId string, ipv4Pool string, ipv6Pool string) (*Network, error) {
state.Lock()
defer state.Unlock()
log.Printf("[core] Creating network %v for subnet %v %v\n", networkId, ipv4Pool, ipv6Pool)
// Find the external interface for this subnet.
extIf := findExternalInterfaceBySubnet(ipv4Pool)
if extIf == nil {
return nil, fmt.Errorf("Pool not found")
}
if extIf.bridgeName == "" {
err := connectExternalInterface(extIf)
if err != nil {
return nil, err
}
}
nw := &Network{
Id: networkId,
ExtIf: extIf,
}
extIf.networks[networkId] = nw
log.Printf("[core] Created network %v on interface %v\n", networkId, extIf.name)
return nw, nil
}
// Deletes a container network.
func DeleteNetwork(nw *Network) error {
state.Lock()
defer state.Unlock()
log.Printf("[core] Deleting network %+v\n", nw)
delete(nw.ExtIf.networks, nw.Id)
// Disconnect the interface if this was the last network using it.
if len(nw.ExtIf.networks) == 0 {
disconnectExternalInterface(nw.ExtIf)
}
log.Printf("[core] Deleted network %+v\n", nw)
return nil
}
// Creates a new endpoint.
func CreateEndpoint(nw *Network, endpointId string, ipAddress string) (*Endpoint, error) {
var containerIf *net.Interface
var ep *Endpoint
// Parse IP address.
ipAddr, ipNet, err := net.ParseCIDR(ipAddress)
ipNet.IP = ipAddr
if err != nil {
return nil, err
}
state.Lock()
defer state.Unlock()
log.Printf("[core] Creating endpoint %v in network %v\n", endpointId, nw.Id)
// Create a veth pair.
contIfName := fmt.Sprintf("%s%s-2", hostInterfacePrefix, endpointId[:7])
hostIfName := fmt.Sprintf("%s%s", hostInterfacePrefix, endpointId[:7])
err = netlink.AddVethPair(contIfName, hostIfName)
if err != nil {
log.Printf("[core] Failed to create veth pair, err=%v\n", err)
return nil, err
}
// Assign IP address to container network interface.
err = netlink.AddIpAddress(contIfName, ipAddr, ipNet)
if err != nil {
goto cleanup
}
// Host interface up.
err = netlink.SetLinkState(hostIfName, true)
if err != nil {
goto cleanup
}
// Connect host interface to the bridge.
err = netlink.SetLinkMaster(hostIfName, nw.ExtIf.bridgeName)
if err != nil {
goto cleanup
}
// Query container network interface info.
containerIf, err = net.InterfaceByName(contIfName)
if err != nil {
goto cleanup
}
// Setup MAC address translation rules for container interface.
err = ebtables.SetupDnatBasedOnIPV4Address(ipAddr.String(), containerIf.HardwareAddr.String())
if err != nil {
goto cleanup
}
ep = &Endpoint{
IPv4Address: *ipNet,
IPv6Address: net.IPNet{},
MacAddress: containerIf.HardwareAddr,
SrcName: contIfName,
DstPrefix: containerInterfacePrefix,
IPv4Gateway: nw.ExtIf.ipv4Gateway,
IPv6Gateway: nw.ExtIf.ipv6Gateway,
}
log.Printf("[core] Created endpoint: %+v\n", ep)
return ep, nil
cleanup:
// Roll back the changes for the endpoint.
netlink.DeleteLink(contIfName)
return nil, err
}
// Deletes an existing endpoint.
func DeleteEndpoint(ep *Endpoint) error {
state.Lock()
defer state.Unlock()
log.Printf("[core] Deleting endpoint: %+v\n", ep)
// Delete veth pair.
netlink.DeleteLink(ep.SrcName)
// Cleanup MAC address translation rules.
err := ebtables.RemoveDnatBasedOnIPV4Address(ep.IPv4Address.IP.String(), ep.MacAddress.String())
log.Printf("[core] Deleted endpoint: %+v\n", ep)
return err
}

178
network/endpoint.go Normal file
Просмотреть файл

@ -0,0 +1,178 @@
// Copyright Microsoft Corp.
// All rights reserved.
package network
import (
"fmt"
"net"
"github.com/Azure/Aqua/ebtables"
"github.com/Azure/Aqua/log"
"github.com/Azure/Aqua/netlink"
)
const (
// Prefix for host virtual network interface names.
hostInterfacePrefix = "veth"
// Prefix for container network interface names.
containerInterfacePrefix = "eth"
)
// Endpoint represents a container network interface.
type endpoint struct {
Id string
SandboxKey string
SrcName string
DstPrefix string
MacAddress net.HardwareAddr
IPv4Address net.IPNet
IPv6Address net.IPNet
IPv4Gateway net.IP
IPv6Gateway net.IP
}
// NewEndpoint creates a new endpoint in the network.
func (nw *network) newEndpoint(endpointId string, ipAddress string) (*endpoint, error) {
var containerIf *net.Interface
var ep *endpoint
var err error
if nw.Endpoints[endpointId] != nil {
return nil, errEndpointExists
}
// Parse IP address.
ipAddr, ipNet, err := net.ParseCIDR(ipAddress)
ipNet.IP = ipAddr
if err != nil {
return nil, err
}
log.Printf("[net] Creating endpoint %v in network %v.", endpointId, nw.Id)
// Create a veth pair.
hostIfName := fmt.Sprintf("%s%s", hostInterfacePrefix, endpointId[:7])
contIfName := fmt.Sprintf("%s%s-2", hostInterfacePrefix, endpointId[:7])
err = netlink.AddVethPair(contIfName, hostIfName)
if err != nil {
log.Printf("[net] Failed to create veth pair, err:%v.", err)
return nil, err
}
// Assign IP address to container network interface.
err = netlink.AddIpAddress(contIfName, ipAddr, ipNet)
if err != nil {
goto cleanup
}
// Host interface up.
err = netlink.SetLinkState(hostIfName, true)
if err != nil {
goto cleanup
}
// Connect host interface to the bridge.
err = netlink.SetLinkMaster(hostIfName, nw.extIf.BridgeName)
if err != nil {
goto cleanup
}
// Query container network interface info.
containerIf, err = net.InterfaceByName(contIfName)
if err != nil {
goto cleanup
}
// Setup MAC address translation rules for container interface.
err = ebtables.SetupDnatBasedOnIPV4Address(ipAddr.String(), containerIf.HardwareAddr.String())
if err != nil {
goto cleanup
}
// Create the endpoint object.
ep = &endpoint{
Id: endpointId,
SrcName: contIfName,
DstPrefix: containerInterfacePrefix,
MacAddress: containerIf.HardwareAddr,
IPv4Address: *ipNet,
IPv6Address: net.IPNet{},
IPv4Gateway: nw.extIf.IPv4Gateway,
IPv6Gateway: nw.extIf.IPv6Gateway,
}
nw.Endpoints[endpointId] = ep
log.Printf("[net] Created endpoint %+v.", ep)
return ep, nil
cleanup:
// Roll back the changes for the endpoint.
netlink.DeleteLink(contIfName)
return nil, err
}
// DeleteEndpoint deletes an existing endpoint from the network.
func (nw *network) deleteEndpoint(endpointId string) error {
ep, err := nw.getEndpoint(endpointId)
if err != nil {
return err
}
log.Printf("[net] Deleting endpoint %+v.", ep)
// Delete veth pair.
netlink.DeleteLink(ep.SrcName)
// Cleanup MAC address translation rules.
err = ebtables.RemoveDnatBasedOnIPV4Address(ep.IPv4Address.IP.String(), ep.MacAddress.String())
// Remove the endpoint object.
delete(nw.Endpoints, endpointId)
log.Printf("[net] Deleted endpoint %+v.", ep)
return nil
}
// GetEndpoint returns the endpoint with the given ID.
func (nw *network) getEndpoint(endpointId string) (*endpoint, error) {
ep := nw.Endpoints[endpointId]
if ep == nil {
return nil, errEndpointNotFound
}
return ep, nil
}
// Attach attaches an endpoint to a sandbox.
func (ep *endpoint) attach(sandboxKey string, options map[string]interface{}) error {
if ep.SandboxKey != "" {
return errEndpointInUse
}
ep.SandboxKey = sandboxKey
log.Printf("[net] Attached endpoint %v to sandbox %v.", ep.Id, sandboxKey)
return nil
}
// Detach detaches an endpoint from its sandbox.
func (ep *endpoint) detach() error {
if ep.SandboxKey == "" {
return errEndpointNotInUse
}
log.Printf("[net] Detached endpoint %v from sandbox %v.", ep.Id, ep.SandboxKey)
ep.SandboxKey = ""
return nil
}

245
network/manager.go Normal file
Просмотреть файл

@ -0,0 +1,245 @@
// Copyright Microsoft Corp.
// All rights reserved.
package network
import (
"sync"
"github.com/Azure/Aqua/common"
"github.com/Azure/Aqua/log"
"github.com/Azure/Aqua/store"
)
const (
// Network store key.
storeKey = "Network"
)
// NetworkManager manages the set of container networking resources.
type networkManager struct {
ExternalInterfaces map[string]*externalInterface
store store.KeyValueStore
sync.Mutex
}
// Creates a new network manager.
func newNetworkManager() (*networkManager, error) {
nm := &networkManager{
ExternalInterfaces: make(map[string]*externalInterface),
}
return nm, nil
}
// Initialize configures network manager.
func (nm *networkManager) Initialize(config *common.PluginConfig) error {
nm.store = config.Store
// Restore persisted state.
err := nm.restore()
return err
}
// Uninitialize cleans up network manager.
func (nm *networkManager) Uninitialize() {
}
// Restore reads network manager state from persistent store.
func (nm *networkManager) restore() error {
// Read any persisted state.
err := nm.store.Read(storeKey, nm)
if err != nil {
if err == store.ErrKeyNotFound {
// Considered successful.
return nil
} else {
log.Printf("[net] Failed to restore state, err:%v\n", err)
return err
}
}
// Populate pointers.
for _, extIf := range nm.ExternalInterfaces {
for _, nw := range extIf.Networks {
nw.extIf = extIf
}
}
log.Printf("[net] Restored state, %+v\n", nm)
return nil
}
// Save writes network manager state to persistent store.
func (nm *networkManager) save() error {
err := nm.store.Write(storeKey, nm)
if err == nil {
log.Printf("[net] Save succeeded.\n")
} else {
log.Printf("[net] Save failed, err:%v\n", err)
}
return err
}
//
// NetworkManager API
//
// Provides atomic stateful wrappers around core networking functionality.
//
// AddExternalInterface adds a host interface to the list of available external interfaces.
func (nm *networkManager) AddExternalInterface(ifName string, subnet string) error {
nm.Lock()
defer nm.Unlock()
err := nm.newExternalInterface(ifName, subnet)
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}
// CreateNetwork creates a new container network.
func (nm *networkManager) CreateNetwork(networkId string, options map[string]interface{}, ipv4Data, ipv6Data []ipamData) error {
nm.Lock()
defer nm.Unlock()
_, err := nm.newNetwork(networkId, options, ipv4Data, ipv6Data)
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}
// DeleteNetwork deletes an existing container network.
func (nm *networkManager) DeleteNetwork(networkId string) error {
nm.Lock()
defer nm.Unlock()
err := nm.deleteNetwork(networkId)
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}
// CreateEndpoint creates a new container endpoint.
func (nm *networkManager) CreateEndpoint(networkId string, endpointId string, ipAddress string) error {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkId)
if err != nil {
return err
}
_, err = nw.newEndpoint(endpointId, ipAddress)
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}
// DeleteEndpoint deletes an existing container endpoint.
func (nm *networkManager) DeleteEndpoint(networkId string, endpointId string) error {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkId)
if err != nil {
return err
}
err = nw.deleteEndpoint(endpointId)
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}
// AttachEndpoint attaches an endpoint to a sandbox.
func (nm *networkManager) AttachEndpoint(networkId string, endpointId string, sandboxKey string) (*endpoint, error) {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkId)
if err != nil {
return nil, err
}
ep, err := nw.getEndpoint(endpointId)
if err != nil {
return nil, err
}
err = ep.attach(sandboxKey, nil)
if err != nil {
return nil, err
}
err = nm.save()
if err != nil {
return nil, err
}
return ep, nil
}
// DetachEndpoint detaches an endpoint from its sandbox.
func (nm *networkManager) DetachEndpoint(networkId string, endpointId string) error {
nm.Lock()
defer nm.Unlock()
nw, err := nm.getNetwork(networkId)
if err != nil {
return err
}
ep, err := nw.getEndpoint(endpointId)
if err != nil {
return err
}
err = ep.detach()
if err != nil {
return err
}
err = nm.save()
if err != nil {
return err
}
return nil
}

Просмотреть файл

@ -4,186 +4,353 @@
package network
import (
"github.com/Azure/Aqua/core"
"fmt"
"net"
"github.com/Azure/Aqua/ebtables"
"github.com/Azure/Aqua/log"
"github.com/Azure/Aqua/netlink"
"golang.org/x/sys/unix"
)
// Network manager manages the set of networks.
type networkManager struct {
networks map[string]*network
const (
// Prefix for bridge names.
bridgePrefix = "aqua"
)
// ExternalInterface represents a host network interface that bridges containers to external networks.
type externalInterface struct {
Name string
Networks map[string]*network
Subnets []string
BridgeName string
MacAddress net.HardwareAddr
IPAddresses []*net.IPNet
Routes []*netlink.Route
IPv4Gateway net.IP
IPv6Gateway net.IP
}
// A container network is a set of endpoints allowed to communicate with each other.
type network struct {
networkId string
endpoints map[string]*endpoint
*core.Network
Id string
Endpoints map[string]*endpoint
extIf *externalInterface
}
// Represents a container endpoint.
type endpoint struct {
endpointId string
networkId string
sandboxKey string
*core.Endpoint
type options map[string]interface{}
// NewExternalInterface adds a host interface to the list of available external interfaces.
func (nm *networkManager) newExternalInterface(ifName string, subnet string) error {
// Check whether the external interface is already configured.
if nm.ExternalInterfaces[ifName] != nil {
return nil
}
// Find the host interface.
hostIf, err := net.InterfaceByName(ifName)
if err != nil {
return err
}
extIf := externalInterface{
Name: ifName,
Networks: make(map[string]*network),
MacAddress: hostIf.HardwareAddr,
IPv4Gateway: net.IPv4zero,
IPv6Gateway: net.IPv6unspecified,
}
extIf.Subnets = append(extIf.Subnets, subnet)
nm.ExternalInterfaces[ifName] = &extIf
log.Printf("[net] Added ExternalInterface %v for subnet %v.", ifName, subnet)
return nil
}
//
// Network Manager
//
// DeleteExternalInterface removes an interface from the list of available external interfaces.
func (nm *networkManager) deleteExternalInterface(ifName string) error {
delete(nm.ExternalInterfaces, ifName)
// Creates a new network manager.
func newNetworkManager() (*networkManager, error) {
return &networkManager{
networks: make(map[string]*network),
}, nil
log.Printf("[net] Deleted ExternalInterface %v.", ifName)
return nil
}
// Creates a new network object.
// FindExternalInterfaceBySubnet finds an external interface connected to the given subnet.
func (nm *networkManager) findExternalInterfaceBySubnet(subnet string) *externalInterface {
for _, extIf := range nm.ExternalInterfaces {
for _, s := range extIf.Subnets {
if s == subnet {
return extIf
}
}
}
return nil
}
// ConnectExternalInterface connects the given host interface to a bridge.
func (nm *networkManager) connectExternalInterface(extIf *externalInterface) error {
var addrs []net.Addr
log.Printf("[net] Connecting interface %v.", extIf.Name)
// Find the external interface.
hostIf, err := net.InterfaceByName(extIf.Name)
if err != nil {
return err
}
// Create the bridge.
bridgeName := fmt.Sprintf("%s%d", bridgePrefix, hostIf.Index)
bridge, err := net.InterfaceByName(bridgeName)
if err != nil {
err = netlink.AddLink(bridgeName, "bridge")
if err != nil {
return err
}
bridge, err = net.InterfaceByName(bridgeName)
if err != nil {
goto cleanup
}
}
// Query the default routes on the external interface.
extIf.Routes, err = netlink.GetIpRoute(&netlink.Route{Dst: &net.IPNet{}, LinkIndex: hostIf.Index})
if err != nil {
log.Printf("[net] Failed to query routes, err:%v.", err)
goto cleanup
}
// Assign external interface's IP addresses to the bridge for host traffic.
addrs, _ = hostIf.Addrs()
for _, addr := range addrs {
ipAddr, ipNet, err := net.ParseCIDR(addr.String())
ipNet.IP = ipAddr
if err != nil {
continue
}
if !ipAddr.IsGlobalUnicast() {
continue
}
extIf.IPAddresses = append(extIf.IPAddresses, ipNet)
log.Printf("[net] Moving IP address %v to bridge %v.", ipNet, bridgeName)
err = netlink.DeleteIpAddress(extIf.Name, ipAddr, ipNet)
if err != nil {
goto cleanup
}
err = netlink.AddIpAddress(bridgeName, ipAddr, ipNet)
if err != nil {
goto cleanup
}
}
// Setup MAC address translation rules for external interface.
err = ebtables.SetupSnatForOutgoingPackets(hostIf.Name, hostIf.HardwareAddr.String())
if err != nil {
goto cleanup
}
err = ebtables.SetupDnatForArpReplies(hostIf.Name)
if err != nil {
goto cleanup
}
// External interface down.
err = netlink.SetLinkState(hostIf.Name, false)
if err != nil {
goto cleanup
}
// Connect the external interface to the bridge.
err = netlink.SetLinkMaster(hostIf.Name, bridgeName)
if err != nil {
goto cleanup
}
// External interface up.
err = netlink.SetLinkState(hostIf.Name, true)
if err != nil {
goto cleanup
}
// Bridge up.
err = netlink.SetLinkState(bridgeName, true)
if err != nil {
goto cleanup
}
// Setup routes on bridge.
for _, route := range extIf.Routes {
if route.Dst == nil {
if route.Family == unix.AF_INET {
extIf.IPv4Gateway = route.Gw
} else if route.Family == unix.AF_INET6 {
extIf.IPv6Gateway = route.Gw
}
}
route.LinkIndex = bridge.Index
err = netlink.AddIpRoute(route)
route.LinkIndex = hostIf.Index
if err != nil {
log.Printf("[net] Failed to add route %+v, err:%v.", route, err)
goto cleanup
}
log.Printf("[net] Added IP route %+v.", route)
}
extIf.BridgeName = bridgeName
log.Printf("[net] Connected interface %v to bridge %v.", extIf.Name, extIf.BridgeName)
return nil
cleanup:
// Roll back the changes for the network.
ebtables.CleanupDnatForArpReplies(extIf.Name)
ebtables.CleanupSnatForOutgoingPackets(extIf.Name, extIf.MacAddress.String())
netlink.DeleteLink(bridgeName)
return err
}
// DisconnectExternalInterface disconnects a host interface from its bridge.
func (nm *networkManager) disconnectExternalInterface(extIf *externalInterface) error {
log.Printf("[net] Disconnecting interface %v.", extIf.Name)
// Cleanup MAC address translation rules.
ebtables.CleanupDnatForArpReplies(extIf.Name)
ebtables.CleanupSnatForOutgoingPackets(extIf.Name, extIf.MacAddress.String())
// Disconnect external interface from its bridge.
err := netlink.SetLinkMaster(extIf.Name, "")
if err != nil {
log.Printf("[net] Failed to disconnect interface %v from bridge, err:%v.", extIf.Name, err)
}
// Delete the bridge.
err = netlink.DeleteLink(extIf.BridgeName)
if err != nil {
log.Printf("[net] Failed to delete bridge %v, err:%v.", extIf.BridgeName, err)
}
extIf.BridgeName = ""
// Restore IP addresses.
for _, addr := range extIf.IPAddresses {
log.Printf("[net] Moving IP address %v to interface %v.", addr, extIf.Name)
err = netlink.AddIpAddress(extIf.Name, addr.IP, addr)
if err != nil {
log.Printf("[net] Failed to add IP address %v, err:%v.", addr, err)
}
}
extIf.IPAddresses = nil
// Restore routes.
for _, route := range extIf.Routes {
log.Printf("[net] Adding IP route %v to interface %v.", route, extIf.Name)
err = netlink.AddIpRoute(route)
if err != nil {
log.Printf("[net] Failed to add IP route %v, err:%v.", route, err)
}
}
extIf.Routes = nil
log.Printf("[net] Disconnected interface %v.", extIf.Name)
return nil
}
// NewNetwork creates a new container network.
func (nm *networkManager) newNetwork(networkId string, options map[string]interface{}, ipv4Data, ipv6Data []ipamData) (*network, error) {
var err error
// Assume single pool per address family.
var ipv4Pool, ipv6Pool string
if len(ipv4Data) > 0 {
ipv4Pool = ipv4Data[0].Pool
}
if nm.networks[networkId] != nil {
if len(ipv6Data) > 0 {
ipv6Pool = ipv6Data[0].Pool
}
log.Printf("[net] Creating network %v for subnet %v %v.", networkId, ipv4Pool, ipv6Pool)
// Find the external interface for this subnet.
extIf := nm.findExternalInterfaceBySubnet(ipv4Pool)
if extIf == nil {
return nil, fmt.Errorf("Pool not found")
}
if extIf.Networks[networkId] != nil {
return nil, errNetworkExists
}
// Connect the external interface if not already connected.
if extIf.BridgeName == "" {
err := nm.connectExternalInterface(extIf)
if err != nil {
return nil, err
}
}
// Create the network object.
nw := &network{
networkId: networkId,
endpoints: make(map[string]*endpoint),
Id: networkId,
Endpoints: make(map[string]*endpoint),
extIf: extIf,
}
pool := ""
if len(ipv4Data) > 0 {
pool = ipv4Data[0].Pool
}
extIf.Networks[networkId] = nw
nw.Network, err = core.CreateNetwork(networkId, pool, "")
if err != nil {
return nil, err
}
nm.networks[networkId] = nw
log.Printf("[net] Created network %v on interface %v.", networkId, extIf.Name)
return nw, nil
}
// Deletes a network object.
// DeleteNetwork deletes an existing container network.
func (nm *networkManager) deleteNetwork(networkId string) error {
nw := nm.networks[networkId]
if nw == nil {
return errNetworkNotFound
}
err := core.DeleteNetwork(nw.Network)
if err != nil {
return err
}
delete(nm.networks, networkId)
return nil
}
// Returns the network with the given ID.
func (nm *networkManager) getNetwork(networkId string) (*network, error) {
nw := nm.networks[networkId]
if nw == nil {
return nil, errNetworkNotFound
}
return nw, nil
}
// Returns the endpoint with the given ID.
func (nm *networkManager) getEndpoint(networkId string, endpointId string) (*endpoint, error) {
nw, err := nm.getNetwork(networkId)
if err != nil {
return nil, err
}
ep, err := nw.getEndpoint(endpointId)
if err != nil {
return nil, errEndpointNotFound
}
return ep, nil
}
//
// Network
//
// Creates a new endpoint in the network.
func (nw *network) newEndpoint(endpointId string, ipAddress string) (*endpoint, error) {
if nw.endpoints[endpointId] != nil {
return nil, errEndpointExists
}
ep := endpoint{
endpointId: endpointId,
networkId: nw.networkId,
}
var err error
ep.Endpoint, err = core.CreateEndpoint(nw.Network, endpointId, ipAddress)
if err != nil {
return nil, err
}
nw.endpoints[endpointId] = &ep
return &ep, nil
}
// Deletes an endpoint from the network.
func (nw *network) deleteEndpoint(endpointId string) error {
ep, err := nw.getEndpoint(endpointId)
if err != nil {
return err
}
err = core.DeleteEndpoint(ep.Endpoint)
if err != nil {
return err
log.Printf("[net] Deleting network %+v.", nw)
// Remove the network object.
delete(nw.extIf.Networks, networkId)
// Disconnect the interface if this was the last network using it.
if len(nw.extIf.Networks) == 0 {
nm.disconnectExternalInterface(nw.extIf)
}
delete(nw.endpoints, endpointId)
log.Printf("[net] Deleted network %+v.", nw)
return nil
}
// Returns the endpoint with the given ID.
func (nw *network) getEndpoint(endpointId string) (*endpoint, error) {
ep := nw.endpoints[endpointId]
if ep == nil {
return nil, errEndpointNotFound
// GetNetwork returns the network with the given ID.
func (nm *networkManager) getNetwork(networkId string) (*network, error) {
for _, extIf := range nm.ExternalInterfaces {
nw, ok := extIf.Networks[networkId]
if ok {
return nw, nil
}
}
return ep, nil
}
//
// Endpoint
//
// Joins an endpoint to a sandbox.
func (ep *endpoint) join(sandboxKey string, options map[string]interface{}) error {
if ep.sandboxKey != "" {
return errEndpointInUse
}
ep.sandboxKey = sandboxKey
return nil
}
// Removes an endpoint from a sandbox.
func (ep *endpoint) leave() error {
if ep.sandboxKey == "" {
return errEndpointNotInUse
}
ep.sandboxKey = ""
return nil
return nil, errNetworkNotFound
}

Просмотреть файл

@ -5,7 +5,6 @@ package network
import (
"net/http"
"sync"
"github.com/Azure/Aqua/common"
"github.com/Azure/Aqua/log"
@ -21,7 +20,6 @@ type netPlugin struct {
*common.Plugin
scope string
nm *networkManager
sync.Mutex
}
type NetPlugin interface {
@ -29,6 +27,10 @@ type NetPlugin interface {
Stop()
}
type NetApi interface {
AddExternalInterface(ifName string, subnet string) error
}
// Creates a new NetPlugin object.
func NewPlugin(name string, version string) (NetPlugin, error) {
// Setup base plugin.
@ -55,7 +57,14 @@ func (plugin *netPlugin) Start(config *common.PluginConfig) error {
// Initialize base plugin.
err := plugin.Initialize(config)
if err != nil {
log.Printf("%s: Failed to initialize base plugin: %v", plugin.Name, err)
log.Printf("[net] Failed to initialize base plugin, err:%v.", err)
return err
}
// Initialize network manager.
err = plugin.nm.Initialize(config)
if err != nil {
log.Printf("[net] Failed to initialize network manager, err:%v.", err)
return err
}
@ -70,15 +79,24 @@ func (plugin *netPlugin) Start(config *common.PluginConfig) error {
listener.AddHandler(leavePath, plugin.leave)
listener.AddHandler(endpointOperInfoPath, plugin.endpointOperInfo)
log.Printf("%s: Plugin started.", plugin.Name)
log.Printf("[net] Plugin started.")
return nil
}
// Stops the plugin.
func (plugin *netPlugin) Stop() {
plugin.nm.Uninitialize()
plugin.Uninitialize()
log.Printf("%s: Plugin stopped.\n", plugin.Name)
log.Printf("[net] Plugin stopped.")
}
//
// NetPlugin internal API
//
func (plugin *netPlugin) AddExternalInterface(ifName string, subnet string) error {
return plugin.nm.AddExternalInterface(ifName, subnet)
}
//
@ -110,10 +128,7 @@ func (plugin *netPlugin) createNetwork(w http.ResponseWriter, r *http.Request) {
}
// Process request.
plugin.Lock()
defer plugin.Unlock()
_, err = plugin.nm.newNetwork(req.NetworkID, req.Options, req.IPv4Data, req.IPv6Data)
err = plugin.nm.CreateNetwork(req.NetworkID, req.Options, req.IPv4Data, req.IPv6Data)
if err != nil {
plugin.SendErrorResponse(w, err)
return
@ -138,10 +153,7 @@ func (plugin *netPlugin) deleteNetwork(w http.ResponseWriter, r *http.Request) {
}
// Process request.
plugin.Lock()
defer plugin.Unlock()
err = plugin.nm.deleteNetwork(req.NetworkID)
err = plugin.nm.DeleteNetwork(req.NetworkID)
if err != nil {
plugin.SendErrorResponse(w, err)
return
@ -171,16 +183,7 @@ func (plugin *netPlugin) createEndpoint(w http.ResponseWriter, r *http.Request)
ipv4Address = req.Interface.Address
}
plugin.Lock()
defer plugin.Unlock()
nw, err := plugin.nm.getNetwork(req.NetworkID)
if err != nil {
plugin.SendErrorResponse(w, err)
return
}
_, err = nw.newEndpoint(req.EndpointID, ipv4Address)
err = plugin.nm.CreateEndpoint(req.NetworkID, req.EndpointID, ipv4Address)
if err != nil {
plugin.SendErrorResponse(w, err)
return
@ -208,16 +211,7 @@ func (plugin *netPlugin) deleteEndpoint(w http.ResponseWriter, r *http.Request)
}
// Process request.
plugin.Lock()
defer plugin.Unlock()
nw, err := plugin.nm.getNetwork(req.NetworkID)
if err != nil {
plugin.SendErrorResponse(w, err)
return
}
err = nw.deleteEndpoint(req.EndpointID)
err = plugin.nm.DeleteEndpoint(req.NetworkID, req.EndpointID)
if err != nil {
plugin.SendErrorResponse(w, err)
return
@ -242,16 +236,7 @@ func (plugin *netPlugin) join(w http.ResponseWriter, r *http.Request) {
}
// Process request.
plugin.Lock()
defer plugin.Unlock()
ep, err := plugin.nm.getEndpoint(req.NetworkID, req.EndpointID)
if err != nil {
plugin.SendErrorResponse(w, err)
return
}
err = ep.join(req.SandboxKey, req.Options)
ep, err := plugin.nm.AttachEndpoint(req.NetworkID, req.EndpointID, req.SandboxKey)
if err != nil {
plugin.SendErrorResponse(w, err)
return
@ -286,16 +271,7 @@ func (plugin *netPlugin) leave(w http.ResponseWriter, r *http.Request) {
}
// Process request.
plugin.Lock()
defer plugin.Unlock()
ep, err := plugin.nm.getEndpoint(req.NetworkID, req.EndpointID)
if err != nil {
plugin.SendErrorResponse(w, err)
return
}
err = ep.leave()
err = plugin.nm.DetachEndpoint(req.NetworkID, req.EndpointID)
if err != nil {
plugin.SendErrorResponse(w, err)
return