Refactor core network logic to NetworkManager
This commit is contained in:
Родитель
78f8d0d54b
Коммит
82c725da46
603
core/logic.go
603
core/logic.go
|
@ -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)
|
||||
}
|
475
core/network.go
475
core/network.go
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче