2017-02-11 03:15:53 +03:00
|
|
|
// Copyright 2017 Microsoft. All rights reserved.
|
|
|
|
// MIT License
|
|
|
|
|
|
|
|
// +build linux
|
2015-12-25 00:09:43 +03:00
|
|
|
|
2016-02-10 23:23:26 +03:00
|
|
|
package network
|
2015-12-02 00:58:59 +03:00
|
|
|
|
|
|
|
import (
|
2016-09-22 01:39:25 +03:00
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
|
2016-10-07 00:40:29 +03:00
|
|
|
"github.com/Azure/azure-container-networking/ebtables"
|
|
|
|
"github.com/Azure/azure-container-networking/log"
|
|
|
|
"github.com/Azure/azure-container-networking/netlink"
|
2016-09-22 01:39:25 +03:00
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Prefix for bridge names.
|
2016-11-22 23:19:54 +03:00
|
|
|
bridgePrefix = "azure"
|
2017-03-07 03:24:20 +03:00
|
|
|
|
|
|
|
// Virtual MAC address used by Azure VNET.
|
|
|
|
virtualMacAddress = "12:34:56:78:9a:bc"
|
2015-12-02 00:58:59 +03:00
|
|
|
)
|
|
|
|
|
2017-02-11 03:15:53 +03:00
|
|
|
// Linux implementation of route.
|
|
|
|
type route netlink.Route
|
2016-11-22 23:31:48 +03:00
|
|
|
|
2017-02-11 03:15:53 +03:00
|
|
|
// NewNetworkImpl creates a new container network.
|
|
|
|
func (nm *networkManager) newNetworkImpl(nwInfo *NetworkInfo, extIf *externalInterface) (*network, error) {
|
2017-03-02 03:59:37 +03:00
|
|
|
// Connect the external interface.
|
2017-03-03 04:52:43 +03:00
|
|
|
switch nwInfo.Mode {
|
2017-03-07 03:24:20 +03:00
|
|
|
case opModeTunnel:
|
|
|
|
fallthrough
|
|
|
|
case opModeBridge:
|
|
|
|
err := nm.connectExternalInterface(extIf, nwInfo)
|
2017-02-11 03:15:53 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-03-02 03:59:37 +03:00
|
|
|
default:
|
2017-03-07 03:24:20 +03:00
|
|
|
return nil, errNetworkModeInvalid
|
2016-09-22 01:39:25 +03:00
|
|
|
}
|
|
|
|
|
2017-02-11 03:15:53 +03:00
|
|
|
// Create the network object.
|
|
|
|
nw := &network{
|
|
|
|
Id: nwInfo.Id,
|
2017-03-03 04:52:43 +03:00
|
|
|
Mode: nwInfo.Mode,
|
2017-02-11 03:15:53 +03:00
|
|
|
Endpoints: make(map[string]*endpoint),
|
|
|
|
extIf: extIf,
|
2016-09-22 01:39:25 +03:00
|
|
|
}
|
|
|
|
|
2017-02-11 03:15:53 +03:00
|
|
|
return nw, nil
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2017-02-11 03:15:53 +03:00
|
|
|
// DeleteNetworkImpl deletes an existing container network.
|
|
|
|
func (nm *networkManager) deleteNetworkImpl(nw *network) error {
|
|
|
|
// Disconnect the interface if this was the last network using it.
|
2017-02-28 12:27:20 +03:00
|
|
|
if len(nw.extIf.Networks) == 1 {
|
2017-02-11 03:15:53 +03:00
|
|
|
nm.disconnectExternalInterface(nw.extIf)
|
2016-09-22 01:39:25 +03:00
|
|
|
}
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-02 03:59:37 +03:00
|
|
|
// SaveIPConfig saves the IP configuration of an interface.
|
|
|
|
func (nm *networkManager) saveIPConfig(hostIf *net.Interface, extIf *externalInterface) error {
|
2017-03-03 04:52:43 +03:00
|
|
|
// Save the default routes on the interface.
|
|
|
|
routes, err := netlink.GetIpRoute(&netlink.Route{Dst: &net.IPNet{}, LinkIndex: hostIf.Index})
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[net] Failed to query routes: %v.", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, r := range routes {
|
|
|
|
if r.Dst == nil {
|
|
|
|
if r.Family == unix.AF_INET {
|
|
|
|
extIf.IPv4Gateway = r.Gw
|
|
|
|
} else if r.Family == unix.AF_INET6 {
|
|
|
|
extIf.IPv6Gateway = r.Gw
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extIf.Routes = append(extIf.Routes, (*route)(r))
|
|
|
|
}
|
|
|
|
|
2017-03-02 03:59:37 +03:00
|
|
|
// Save global unicast IP addresses on the interface.
|
|
|
|
addrs, err := 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] Deleting IP address %v from interface %v.", ipNet, hostIf.Name)
|
|
|
|
|
|
|
|
err = netlink.DeleteIpAddress(hostIf.Name, ipAddr, ipNet)
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[net] Saved interface IP configuration %+v.", extIf)
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// ApplyIPConfig applies a previously saved IP configuration to an interface.
|
|
|
|
func (nm *networkManager) applyIPConfig(extIf *externalInterface, targetIf *net.Interface) error {
|
|
|
|
// Add IP addresses.
|
|
|
|
for _, addr := range extIf.IPAddresses {
|
2017-03-03 04:52:43 +03:00
|
|
|
log.Printf("[net] Adding IP address %v to interface %v.", addr, targetIf.Name)
|
2017-03-02 03:59:37 +03:00
|
|
|
|
2017-03-03 04:52:43 +03:00
|
|
|
err := netlink.AddIpAddress(targetIf.Name, addr.IP, addr)
|
2017-03-02 03:59:37 +03:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("[net] Failed to add IP address %v: %v.", addr, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add IP routes.
|
|
|
|
for _, route := range extIf.Routes {
|
|
|
|
route.LinkIndex = targetIf.Index
|
|
|
|
|
|
|
|
log.Printf("[net] Adding IP route %+v.", route)
|
|
|
|
|
|
|
|
err := netlink.AddIpRoute((*netlink.Route)(route))
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[net] Failed to add IP route %v: %v.", route, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-07 03:24:20 +03:00
|
|
|
// AddBridgeRules adds bridge frame table rules for container traffic.
|
2017-07-15 21:03:56 +03:00
|
|
|
func (nm *networkManager) addBridgeRules(extIf *externalInterface, hostIf *net.Interface, bridgeName string, opMode string) error {
|
2017-03-07 03:24:20 +03:00
|
|
|
// Add SNAT rule to translate container egress traffic.
|
|
|
|
log.Printf("[net] Adding SNAT rule for egress traffic on %v.", hostIf.Name)
|
|
|
|
err := ebtables.SetSnatForInterface(hostIf.Name, hostIf.HardwareAddr, ebtables.Append)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add ARP reply rule for host primary IP address.
|
|
|
|
// ARP requests for all IP addresses are forwarded to the SDN fabric, but fabric
|
|
|
|
// doesn't respond to ARP requests from the VM for its own primary IP address.
|
|
|
|
primary := extIf.IPAddresses[0].IP
|
|
|
|
log.Printf("[net] Adding ARP reply rule for primary IP address %v.", primary)
|
|
|
|
err = ebtables.SetArpReply(primary, hostIf.HardwareAddr, ebtables.Append)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add DNAT rule to forward ARP replies to container interfaces.
|
|
|
|
log.Printf("[net] Adding DNAT rule for ingress ARP traffic on interface %v.", hostIf.Name)
|
|
|
|
err = ebtables.SetDnatForArpReplies(hostIf.Name, ebtables.Append)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable VEPA for host policy enforcement if necessary.
|
|
|
|
if opMode == opModeTunnel {
|
|
|
|
log.Printf("[net] Enabling VEPA mode for %v.", hostIf.Name)
|
2017-07-15 21:03:56 +03:00
|
|
|
err = ebtables.SetVepaMode(bridgeName, commonInterfacePrefix, virtualMacAddress, ebtables.Append)
|
2017-03-07 03:24:20 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteBridgeRules deletes bridge rules for container traffic.
|
|
|
|
func (nm *networkManager) deleteBridgeRules(extIf *externalInterface) {
|
2017-07-15 21:03:56 +03:00
|
|
|
ebtables.SetVepaMode(extIf.BridgeName, commonInterfacePrefix, virtualMacAddress, ebtables.Delete)
|
2017-03-07 03:24:20 +03:00
|
|
|
ebtables.SetDnatForArpReplies(extIf.Name, ebtables.Delete)
|
|
|
|
ebtables.SetArpReply(extIf.IPAddresses[0].IP, extIf.MacAddress, ebtables.Delete)
|
|
|
|
ebtables.SetSnatForInterface(extIf.Name, extIf.MacAddress, ebtables.Delete)
|
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// ConnectExternalInterface connects the given host interface to a bridge.
|
2017-03-07 03:24:20 +03:00
|
|
|
func (nm *networkManager) connectExternalInterface(extIf *externalInterface, nwInfo *NetworkInfo) error {
|
2016-09-22 01:39:25 +03:00
|
|
|
log.Printf("[net] Connecting interface %v.", extIf.Name)
|
|
|
|
|
2017-03-02 03:59:37 +03:00
|
|
|
// Check whether this interface is already connected.
|
|
|
|
if extIf.BridgeName != "" {
|
|
|
|
log.Printf("[net] Interface is already connected to bridge %v.", extIf.BridgeName)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// Find the external interface.
|
|
|
|
hostIf, err := net.InterfaceByName(extIf.Name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2016-11-22 23:31:48 +03:00
|
|
|
// If a bridge name is not specified, generate one based on the external interface index.
|
2017-03-07 03:24:20 +03:00
|
|
|
bridgeName := nwInfo.BridgeName
|
2016-11-22 23:31:48 +03:00
|
|
|
if bridgeName == "" {
|
|
|
|
bridgeName = fmt.Sprintf("%s%d", bridgePrefix, hostIf.Index)
|
|
|
|
}
|
2016-11-22 23:19:54 +03:00
|
|
|
|
|
|
|
// Check if the bridge already exists.
|
2016-09-22 01:39:25 +03:00
|
|
|
bridge, err := net.InterfaceByName(bridgeName)
|
|
|
|
if err != nil {
|
2016-11-22 23:19:54 +03:00
|
|
|
// Create the bridge.
|
|
|
|
log.Printf("[net] Creating bridge %v.", bridgeName)
|
2017-02-28 12:27:20 +03:00
|
|
|
|
|
|
|
link := netlink.BridgeLink{
|
|
|
|
LinkInfo: netlink.LinkInfo{
|
|
|
|
Type: netlink.LINK_TYPE_BRIDGE,
|
|
|
|
Name: bridgeName,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err = netlink.AddLink(&link)
|
2016-09-22 01:39:25 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bridge, err = net.InterfaceByName(bridgeName)
|
|
|
|
if err != nil {
|
|
|
|
goto cleanup
|
|
|
|
}
|
2016-11-22 23:19:54 +03:00
|
|
|
} else {
|
|
|
|
// Use the existing bridge.
|
|
|
|
log.Printf("[net] Found existing bridge %v.", bridgeName)
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2017-03-02 03:59:37 +03:00
|
|
|
// Save host IP configuration.
|
|
|
|
err = nm.saveIPConfig(hostIf, extIf)
|
2016-09-22 01:39:25 +03:00
|
|
|
if err != nil {
|
2017-03-02 03:59:37 +03:00
|
|
|
log.Printf("[net] Failed to save IP configuration for interface %v: %v.", hostIf.Name, err)
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2017-03-07 03:24:20 +03:00
|
|
|
// Add the bridge rules.
|
2017-07-15 21:03:56 +03:00
|
|
|
err = nm.addBridgeRules(extIf, hostIf, bridgeName, nwInfo.Mode)
|
2016-09-22 01:39:25 +03:00
|
|
|
if err != nil {
|
|
|
|
goto cleanup
|
|
|
|
}
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// External interface down.
|
2016-11-22 23:19:54 +03:00
|
|
|
log.Printf("[net] Setting link %v state down.", hostIf.Name)
|
2016-09-22 01:39:25 +03:00
|
|
|
err = netlink.SetLinkState(hostIf.Name, false)
|
|
|
|
if err != nil {
|
|
|
|
goto cleanup
|
|
|
|
}
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// Connect the external interface to the bridge.
|
2016-11-22 23:19:54 +03:00
|
|
|
log.Printf("[net] Setting link %v master %v.", hostIf.Name, bridgeName)
|
2016-09-22 01:39:25 +03:00
|
|
|
err = netlink.SetLinkMaster(hostIf.Name, bridgeName)
|
|
|
|
if err != nil {
|
|
|
|
goto cleanup
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// External interface up.
|
2016-11-22 23:19:54 +03:00
|
|
|
log.Printf("[net] Setting link %v state up.", hostIf.Name)
|
2016-09-22 01:39:25 +03:00
|
|
|
err = netlink.SetLinkState(hostIf.Name, true)
|
2016-06-03 05:02:07 +03:00
|
|
|
if err != nil {
|
2016-09-22 01:39:25 +03:00
|
|
|
goto cleanup
|
|
|
|
}
|
|
|
|
|
2017-03-31 16:45:28 +03:00
|
|
|
// External interface hairpin on.
|
|
|
|
log.Printf("[net] Setting link %v hairpin on.", hostIf.Name)
|
|
|
|
err = netlink.SetLinkHairpin(hostIf.Name, true)
|
|
|
|
if err != nil {
|
|
|
|
goto cleanup
|
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// Bridge up.
|
2016-11-22 23:19:54 +03:00
|
|
|
log.Printf("[net] Setting link %v state up.", bridgeName)
|
2016-09-22 01:39:25 +03:00
|
|
|
err = netlink.SetLinkState(bridgeName, true)
|
|
|
|
if err != nil {
|
|
|
|
goto cleanup
|
|
|
|
}
|
|
|
|
|
2017-03-02 03:59:37 +03:00
|
|
|
// Apply IP configuration to the bridge for host traffic.
|
|
|
|
err = nm.applyIPConfig(extIf, bridge)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[net] Failed to apply interface IP configuration: %v.", err)
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
extIf.BridgeName = bridgeName
|
|
|
|
|
|
|
|
log.Printf("[net] Connected interface %v to bridge %v.", extIf.Name, extIf.BridgeName)
|
2016-06-03 05:02:07 +03:00
|
|
|
|
|
|
|
return nil
|
2015-12-02 00:58:59 +03:00
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
cleanup:
|
2016-11-22 23:19:54 +03:00
|
|
|
log.Printf("[net] Connecting interface %v failed, err:%v.", extIf.Name, err)
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// Roll back the changes for the network.
|
2017-03-07 03:24:20 +03:00
|
|
|
nm.deleteBridgeRules(extIf)
|
2016-09-22 01:39:25 +03:00
|
|
|
netlink.DeleteLink(bridgeName)
|
|
|
|
|
|
|
|
return err
|
2015-12-02 00:58:59 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// DisconnectExternalInterface disconnects a host interface from its bridge.
|
|
|
|
func (nm *networkManager) disconnectExternalInterface(extIf *externalInterface) error {
|
|
|
|
log.Printf("[net] Disconnecting interface %v.", extIf.Name)
|
|
|
|
|
2017-03-07 03:24:20 +03:00
|
|
|
// Delete bridge rules set on the external interface.
|
|
|
|
nm.deleteBridgeRules(extIf)
|
2016-09-22 01:39:25 +03:00
|
|
|
|
|
|
|
// Disconnect external interface from its bridge.
|
|
|
|
err := netlink.SetLinkMaster(extIf.Name, "")
|
2016-06-03 05:02:07 +03:00
|
|
|
if err != nil {
|
2016-09-22 01:39:25 +03:00
|
|
|
log.Printf("[net] Failed to disconnect interface %v from bridge, err:%v.", extIf.Name, err)
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// Delete the bridge.
|
|
|
|
err = netlink.DeleteLink(extIf.BridgeName)
|
2016-06-03 05:02:07 +03:00
|
|
|
if err != nil {
|
2016-09-22 01:39:25 +03:00
|
|
|
log.Printf("[net] Failed to delete bridge %v, err:%v.", extIf.BridgeName, err)
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
extIf.BridgeName = ""
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2017-03-02 03:59:37 +03:00
|
|
|
// Restore IP configuration.
|
|
|
|
hostIf, _ := net.InterfaceByName(extIf.Name)
|
|
|
|
err = nm.applyIPConfig(extIf, hostIf)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("[net] Failed to apply IP configuration: %v.", err)
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
extIf.IPAddresses = nil
|
|
|
|
extIf.Routes = nil
|
|
|
|
|
|
|
|
log.Printf("[net] Disconnected interface %v.", extIf.Name)
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
return nil
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|