2017-02-11 03:15:53 +03:00
|
|
|
// Copyright 2017 Microsoft. All rights reserved.
|
|
|
|
// MIT License
|
|
|
|
|
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"
|
2018-07-06 21:45:47 +03:00
|
|
|
"strconv"
|
2018-02-22 22:22:47 +03:00
|
|
|
"strings"
|
2016-09-22 01:39:25 +03:00
|
|
|
|
2020-04-16 08:30:48 +03:00
|
|
|
"github.com/Azure/azure-container-networking/iptables"
|
2021-10-16 00:28:37 +03:00
|
|
|
"github.com/Azure/azure-container-networking/netio"
|
2016-10-07 00:40:29 +03:00
|
|
|
"github.com/Azure/azure-container-networking/netlink"
|
2021-10-16 00:28:37 +03:00
|
|
|
"github.com/Azure/azure-container-networking/network/networkutils"
|
2021-09-20 22:04:35 +03:00
|
|
|
"github.com/Azure/azure-container-networking/ovsctl"
|
2019-05-01 02:27:48 +03:00
|
|
|
"github.com/Azure/azure-container-networking/platform"
|
2023-10-27 01:54:08 +03:00
|
|
|
"github.com/pkg/errors"
|
2023-09-16 03:14:44 +03:00
|
|
|
"go.uber.org/zap"
|
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.
|
2021-09-02 02:28:17 +03:00
|
|
|
virtualMacAddress = "12:34:56:78:9a:bc"
|
|
|
|
versionID = "VERSION_ID"
|
|
|
|
distroID = "ID"
|
|
|
|
ubuntuStr = "ubuntu"
|
|
|
|
dnsServersStr = "DNS Servers"
|
|
|
|
dnsDomainStr = "DNS Domain"
|
|
|
|
ubuntuVersion17 = 17
|
|
|
|
// OptVethName key for veth name option
|
|
|
|
OptVethName = "vethname"
|
|
|
|
// SnatBridgeIPKey key for the SNAT bridge
|
|
|
|
SnatBridgeIPKey = "snatBridgeIP"
|
|
|
|
// LocalIPKey key for local IP
|
|
|
|
LocalIPKey = "localIP"
|
|
|
|
// InfraVnetIPKey key for infra vnet
|
|
|
|
InfraVnetIPKey = "infraVnetIP"
|
2023-10-27 01:54:08 +03:00
|
|
|
// Ubuntu Release Version for checking which command to use.
|
|
|
|
Ubuntu22 = "22.04"
|
2019-05-01 02:27:48 +03:00
|
|
|
)
|
2018-08-19 00:50:49 +03:00
|
|
|
|
2019-05-01 02:27:48 +03:00
|
|
|
const (
|
|
|
|
lineDelimiter = "\n"
|
|
|
|
colonDelimiter = ":"
|
|
|
|
dotDelimiter = "."
|
2015-12-02 00:58:59 +03:00
|
|
|
)
|
|
|
|
|
2021-09-20 21:57:12 +03:00
|
|
|
var errorNetworkManager = errors.New("Network_linux pkg error")
|
|
|
|
|
|
|
|
func newErrorNetworkManager(errStr string) error {
|
|
|
|
return fmt.Errorf("%w : %s", errorNetworkManager, errStr)
|
|
|
|
}
|
|
|
|
|
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.
|
2021-10-19 20:54:49 +03:00
|
|
|
var (
|
|
|
|
vlanid int
|
|
|
|
ifName string
|
|
|
|
)
|
2018-07-06 21:45:47 +03:00
|
|
|
opt, _ := nwInfo.Options[genericData].(map[string]interface{})
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("opt options", zap.Any("opt", opt), zap.Any("options", nwInfo.Options))
|
2018-07-06 21:45:47 +03:00
|
|
|
|
2017-03-03 04:52:43 +03:00
|
|
|
switch nwInfo.Mode {
|
2017-03-07 03:24:20 +03:00
|
|
|
case opModeTunnel:
|
|
|
|
fallthrough
|
|
|
|
case opModeBridge:
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("create bridge")
|
2021-10-19 20:54:49 +03:00
|
|
|
ifName = extIf.BridgeName
|
2018-07-06 21:45:47 +03:00
|
|
|
if err := nm.connectExternalInterface(extIf, nwInfo); err != nil {
|
2017-02-11 03:15:53 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
2018-07-06 21:45:47 +03:00
|
|
|
|
|
|
|
if opt != nil && opt[VlanIDKey] != nil {
|
|
|
|
vlanid, _ = strconv.Atoi(opt[VlanIDKey].(string))
|
|
|
|
}
|
2019-01-05 03:19:36 +03:00
|
|
|
case opModeTransparent:
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Transparent mode")
|
2021-10-19 20:54:49 +03:00
|
|
|
ifName = extIf.Name
|
2021-10-16 00:28:37 +03:00
|
|
|
if nwInfo.IPV6Mode != "" {
|
|
|
|
nu := networkutils.NewNetworkUtils(nm.netlink, nm.plClient)
|
|
|
|
if err := nu.EnableIPV6Forwarding(); err != nil {
|
|
|
|
return nil, fmt.Errorf("Ipv6 forwarding failed: %w", err)
|
|
|
|
}
|
2021-09-20 21:57:12 +03:00
|
|
|
}
|
feat: Add SNAT bridge to Native, decouple SNAT bridge (#1506)
* Native Endpoint Client Add Endpoints
* AddEndpointRules, ConfigureContainerInterfacesAndRoutes
* Changed interface names, log statements
nw.extIf.Name > eth0 (eth0)
eth0.vlanid > eth0.X (eth0.1)
%s%s hostIfName > vnet (A1veth0)
%s%s-2 contIfName > container (B1veth0)
* Renaming, using lib to set ns
* Namespace "path" is /var/run/netns/<NS>
* Loopback set up, Remove auto kernel subnet route
* Cannot set link to up if it's in another NS
* Multiple containers on same VNET NS
* Delete Endpoint routes on Delete
* Minimizing netns usage
* Moving NS Exec Code
* Further minimized netns.Set usage
* Moved helper methods down, drafted tests
* Removed DevName from Route Info, more tests
* Test existing vnet ns, delete endpoint
* NetNS interface for testing
* Separated tests by namespace
* Endpoints delete if they cannot be moved into NS
* Namespace netns tests
* Added Native Client to deleteEndpointImpl
* Deletion of Endpoints Impl and Tests
* Cleaned code (Tests ok)
* Moved mock/netns to package (Tests ok)
* Fixing Netns (wip)
Moved netnsinterface to consumer package (network).
Removed "Netns" from "NewNetns" and "NewMockNetns" as it is unambiguous.
Changed uintptr to int and casted the int to uintptr when needed later.
* Using errors.Wrap for error context (wip)
* Removed sentence case (wip)
* Removing variable predeclaration
* Removed NewNativeEndpointClient
Directly instantiating struct because nothing special happens in NewNativeEndpointClient
* Removed generics from ExecuteInNS
* Removed uintptr from mocknetns, tests compile
Forgot to remove uintptr from mocknetns
* Fix tests, lint
* Fixes from linter
Works on VMSS
* Replacing references to ethX with vlan veth
* Removed unnecessary log
* Removed unnecessary mac, fix tests
* Mockns method name enum
* Unable to use GetNetworkInterfaceByName due to NS
If I use GetNetworkInterface, I need to be in the vnet NS, but that means I will need to call ExecuteInNS, which causes tests to fail.
* Fixes from linter
* Assume if NS exists, vlan veth exists
Tests ok
* Fixes for Linter
* Snat refactor
* Fix delete tests
* Fix delete tests bug
* More snat refactor
* Breaking, prepping for Native Snat
Delete native endpoint snat route linux to remove errors and in theory, ovs should work fine again.
* Go mod tidy for linting
Hopefully this fixes the windows lint error
* Add fields to native endpoint client for snat
* Using New() func to create Native Client
Creation of the native endpoint client is too complicated to directly instantiate.
* Snat defaults
* Insert SNAT entry points
* Native Snat error handling
* Breaking, decouple ovsctl from snat
Proposed Solution implementation
Moved ovsctlClient.AddPortOnOVSBridge to ovs_endpoint_snatroute_linux.go. Removed ovsctlclient from NewSnatClient. Removed ovsctlClient from testing file.
* Delete unecessary ovssnat files
* No lint on vishvananda netns
Maybe this will fix the windows linter?
* Build linux only for netns package
Maybe this fixes the linter error?
* Remove nolint to see if linter fails
* Breaking, removed bridgeName
bridgeName refers to the OVS Switch I believe
* If native uses snat bridge, should also get IP
* Breaking, Decouple or Wrap snat route
* Check to see if snat triggered
* Snat behaviors specific to ovs/native
* Pass the pointer
Add/Delete ok
* Renaming to make consts public
* Breaking, moving ovs specific parts of snat to ovs
* Remove enable infra vnet (Tests ok)
Tested:
Allow Host to NC only
Allow NC to Host only
Allow both
Wget
Ping between containers
Warning: Enable snat is still hard coded to true!!!
* Move add port to after exists() check
* Moved netns interface to caller, generalized tests
Tests ok, Native ok
* Typos
* Reordered if statement, unwrapped arp
Tests ok, ping ok, wget ok
* Linted, wrapping errors
* Go fumpt entire network package
* Code markers removed, clean (Tests ok)
OVS & Native:
- Ping between two containers same VM, no packets on bridge
- Ping between two containers diff VM, no packets on bridge
- Ping other container not in vnet, no packets on bridge
- Ping snat to container, packets on bridge
- Ping container to snat, packets on bridge
- Tcpdump confirmed on azSnatBr
- Deletion of containers deletes appropriate interfaces
* Renamed veth, fixed logs
* Made deleteEndpoints logic clearer, renamed error
* Renamed eth0 to primaryHostIfName, vlanEth to vlanIf
* Deleted debug log
* Corrected merge (hardware addr) (Tests ok)
* Renamed vlan veth to hostExtIf_vlanID, Disabled RA
eth0.2 makes disable RA look for a folder eth0 and then another sub folder "2". ("eth0/2") However, it should look for a folder named "eth0.2" literally. To solve this, we change the naming scheme to use an underscore instead. (Tests ok)
* Renamed Native to TransparentVlan
Confirmed basic functionality on VM with correct mode
* Make file updated
* Create azure-windows-multitenancy-transparent-vlan.conflist
* Unified snat err format
* Rename to transparent-vlan
* Route table support added to local netlink
* Moved SNAT to end of function
* Defer deleting vlan interface on failure
2022-08-10 23:50:26 +03:00
|
|
|
case opModeTransparentVlan:
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Transparent vlan mode")
|
feat: Add native linux endpoint client to prep removing OVS (#1471)
* Native Endpoint Client Add Endpoints
* AddEndpointRules, ConfigureContainerInterfacesAndRoutes
* Changed interface names, log statements
nw.extIf.Name > eth0 (eth0)
eth0.vlanid > eth0.X (eth0.1)
%s%s hostIfName > vnet (A1veth0)
%s%s-2 contIfName > container (B1veth0)
* Renaming, using lib to set ns
* Namespace "path" is /var/run/netns/<NS>
* Loopback set up, Remove auto kernel subnet route
* Cannot set link to up if it's in another NS
* Multiple containers on same VNET NS
* Delete Endpoint routes on Delete
* Minimizing netns usage
* Moving NS Exec Code
* Further minimized netns.Set usage
* Moved helper methods down, drafted tests
* Removed DevName from Route Info, more tests
* Test existing vnet ns, delete endpoint
* NetNS interface for testing
* Separated tests by namespace
* Endpoints delete if they cannot be moved into NS
* Namespace netns tests
* Added Native Client to deleteEndpointImpl
* Deletion of Endpoints Impl and Tests
* Cleaned code (Tests ok)
* Moved mock/netns to package (Tests ok)
* Fixing Netns (wip)
Moved netnsinterface to consumer package (network).
Removed "Netns" from "NewNetns" and "NewMockNetns" as it is unambiguous.
Changed uintptr to int and casted the int to uintptr when needed later.
* Using errors.Wrap for error context (wip)
* Removed sentence case (wip)
* Removing variable predeclaration
* Removed NewNativeEndpointClient
Directly instantiating struct because nothing special happens in NewNativeEndpointClient
* Removed generics from ExecuteInNS
* Removed uintptr from mocknetns, tests compile
Forgot to remove uintptr from mocknetns
* Fix tests, lint
* Fixes from linter
Works on VMSS
* Replacing references to ethX with vlan veth
* Removed unnecessary log
* Removed unnecessary mac, fix tests
* Mockns method name enum
* Unable to use GetNetworkInterfaceByName due to NS
If I use GetNetworkInterface, I need to be in the vnet NS, but that means I will need to call ExecuteInNS, which causes tests to fail.
* Fixes from linter
* Assume if NS exists, vlan veth exists
Tests ok
* Fixes for Linter
* Fix delete tests
* Fix delete tests bug
* Go mod tidy for linting
Hopefully this fixes the windows lint error
* No lint on vishvananda netns
Maybe this will fix the windows linter?
* Build linux only for netns package
Maybe this fixes the linter error?
* Remove nolint to see if linter fails
* Moved netns interface to caller, generalized tests
Tests ok, Native ok
* Typos
* Reordered if statement, unwrapped arp
Tests ok, ping ok, wget ok
* Renamed veth, fixed logs
* Made deleteEndpoints logic clearer, renamed error
* Renamed eth0 to primaryHostIfName, vlanEth to vlanIf
2022-08-03 00:54:10 +03:00
|
|
|
ifName = extIf.Name
|
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
|
|
|
}
|
|
|
|
|
2021-10-19 20:54:49 +03:00
|
|
|
err := nm.handleCommonOptions(ifName, nwInfo)
|
|
|
|
if err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("handleCommonOptions failed with", zap.Error(err))
|
2021-10-19 20:54:49 +03:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-11 03:15:53 +03:00
|
|
|
// Create the network object.
|
|
|
|
nw := &network{
|
2018-07-06 21:45:47 +03:00
|
|
|
Id: nwInfo.Id,
|
|
|
|
Mode: nwInfo.Mode,
|
|
|
|
Endpoints: make(map[string]*endpoint),
|
|
|
|
extIf: extIf,
|
|
|
|
VlanId: vlanid,
|
2018-08-19 00:50:49 +03:00
|
|
|
DNS: nwInfo.DNS,
|
2018-07-06 21:45:47 +03:00
|
|
|
EnableSnatOnHost: nwInfo.EnableSnatOnHost,
|
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
|
|
|
}
|
|
|
|
|
2021-10-19 20:54:49 +03:00
|
|
|
func (nm *networkManager) handleCommonOptions(ifName string, nwInfo *NetworkInfo) error {
|
2020-11-17 01:24:00 +03:00
|
|
|
var err error
|
|
|
|
if routes, exists := nwInfo.Options[RoutesKey]; exists {
|
2021-10-19 20:54:49 +03:00
|
|
|
err = addRoutes(nm.netlink, nm.netio, ifName, routes.([]RouteInfo))
|
2020-11-17 01:24:00 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if iptcmds, exists := nwInfo.Options[IPTablesKey]; exists {
|
2021-09-20 21:57:12 +03:00
|
|
|
err = nm.addToIptables(iptcmds.([]iptables.IPTableEntry))
|
2020-11-17 01:24:00 +03:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-11 03:15:53 +03:00
|
|
|
// DeleteNetworkImpl deletes an existing container network.
|
|
|
|
func (nm *networkManager) deleteNetworkImpl(nw *network) error {
|
2018-07-06 21:45:47 +03:00
|
|
|
var networkClient NetworkClient
|
|
|
|
|
|
|
|
if nw.VlanId != 0 {
|
2021-10-16 00:28:37 +03:00
|
|
|
networkClient = NewOVSClient(nw.extIf.BridgeName, nw.extIf.Name, ovsctl.NewOvsctl(), nm.netlink, nm.plClient)
|
2018-07-06 21:45:47 +03:00
|
|
|
} else {
|
2021-10-16 00:28:37 +03:00
|
|
|
networkClient = NewLinuxBridgeClient(nw.extIf.BridgeName, nw.extIf.Name, NetworkInfo{}, nm.netlink, nm.plClient)
|
2018-07-06 21:45:47 +03:00
|
|
|
}
|
|
|
|
|
2017-02-11 03:15:53 +03:00
|
|
|
// 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 {
|
2018-07-06 21:45:47 +03:00
|
|
|
nm.disconnectExternalInterface(nw.extIf, networkClient)
|
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
|
|
|
|
}
|
|
|
|
|
2023-04-18 00:26:00 +03:00
|
|
|
// SaveIPConfig saves the IP configuration of an interface.
|
2017-03-02 03:59:37 +03:00
|
|
|
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.
|
2021-09-20 21:57:12 +03:00
|
|
|
routes, err := nm.netlink.GetIPRoute(&netlink.Route{Dst: &net.IPNet{}, LinkIndex: hostIf.Index})
|
2017-03-03 04:52:43 +03:00
|
|
|
if err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Failed to query routes", zap.Error(err))
|
2017-03-03 04:52:43 +03:00
|
|
|
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)
|
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Deleting IP address from interface", zap.Any("ipNet", ipNet), zap.String("hostInfName", hostIf.Name))
|
2017-03-02 03:59:37 +03:00
|
|
|
|
2021-09-20 21:57:12 +03:00
|
|
|
err = nm.netlink.DeleteIPAddress(hostIf.Name, ipAddr, ipNet)
|
2017-03-02 03:59:37 +03:00
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Saved interface IP configuration", zap.Any("extIf", extIf))
|
2017-03-02 03:59:37 +03:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-05-01 02:27:48 +03:00
|
|
|
func getMajorVersion(version string) (int, error) {
|
|
|
|
versionSplit := strings.Split(version, dotDelimiter)
|
|
|
|
if len(versionSplit) > 0 {
|
|
|
|
retrieved_version, err := strconv.Atoi(versionSplit[0])
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return retrieved_version, err
|
|
|
|
}
|
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
return 0, fmt.Errorf("Error getting major version") //nolint
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func isGreaterOrEqaulUbuntuVersion(versionToMatch int) bool {
|
|
|
|
osInfo, err := platform.GetOSDetails()
|
|
|
|
if err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Unable to get OS Details", zap.Error(err))
|
2019-05-01 02:27:48 +03:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("OSInfo", zap.Any("osInfo", osInfo))
|
2019-05-01 02:27:48 +03:00
|
|
|
|
|
|
|
version := osInfo[versionID]
|
|
|
|
distro := osInfo[distroID]
|
|
|
|
|
|
|
|
if strings.EqualFold(distro, ubuntuStr) {
|
|
|
|
version = strings.Trim(version, "\"")
|
|
|
|
retrieved_version, err := getMajorVersion(version)
|
|
|
|
if err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Not setting dns. Unable to retrieve major version", zap.Error(err))
|
2019-05-01 02:27:48 +03:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if retrieved_version >= versionToMatch {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-10-27 01:54:08 +03:00
|
|
|
func (nm *networkManager) systemVersion() (string, error) {
|
|
|
|
osVersion, err := nm.plClient.ExecuteCommand("lsb_release -rs")
|
|
|
|
if err != nil {
|
|
|
|
return osVersion, errors.Wrap(err, "error retrieving the system distribution version")
|
|
|
|
}
|
|
|
|
return osVersion, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nm *networkManager) addDomain(ifName, domain string) (string, error) {
|
|
|
|
osVersion, err := nm.systemVersion()
|
|
|
|
if err != nil {
|
|
|
|
return osVersion, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmd string
|
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(osVersion, Ubuntu22):
|
|
|
|
cmd = fmt.Sprintf("resolvectl domain %s %s", ifName, domain)
|
|
|
|
default:
|
|
|
|
cmd = fmt.Sprintf("systemd-resolve --interface %s --set-domain %s", ifName, domain)
|
|
|
|
}
|
|
|
|
return cmd, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nm *networkManager) addDNSServers(ifName string, dnsServers []string) (string, error) {
|
|
|
|
osVersion, err := nm.systemVersion()
|
|
|
|
if err != nil {
|
|
|
|
return osVersion, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var cmd string
|
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(osVersion, Ubuntu22):
|
|
|
|
cmd = fmt.Sprintf("resolvectl dns %s %s", ifName, strings.Join(dnsServers, " "))
|
|
|
|
default:
|
|
|
|
cmd = fmt.Sprintf("systemd-resolve --interface %s %s", ifName, strings.Join(dnsServers, "--set-dns "))
|
|
|
|
}
|
|
|
|
return cmd, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nm *networkManager) ifNameStatus(ifName string) (string, error) {
|
|
|
|
osVersion, err := nm.systemVersion()
|
|
|
|
if err != nil {
|
|
|
|
return osVersion, err
|
|
|
|
}
|
|
|
|
var cmd string
|
|
|
|
switch {
|
|
|
|
case strings.HasPrefix(osVersion, Ubuntu22):
|
|
|
|
cmd = fmt.Sprintf("resolvectl status %s", ifName)
|
|
|
|
default:
|
|
|
|
cmd = fmt.Sprintf("systemd-resolve --status %s", ifName)
|
|
|
|
}
|
|
|
|
return cmd, nil
|
|
|
|
}
|
|
|
|
|
2023-10-13 22:08:58 +03:00
|
|
|
func (nm *networkManager) readDNSInfo(ifName string) (DNSInfo, error) {
|
2019-05-03 01:51:40 +03:00
|
|
|
var dnsInfo DNSInfo
|
|
|
|
|
2023-10-27 01:54:08 +03:00
|
|
|
cmd, err := nm.ifNameStatus(ifName)
|
|
|
|
if err != nil {
|
|
|
|
return dnsInfo, errors.Wrap(err, "Error generating interface name status cmd")
|
|
|
|
}
|
|
|
|
|
2023-10-13 22:08:58 +03:00
|
|
|
out, err := nm.plClient.ExecuteCommand(cmd)
|
2019-05-01 02:27:48 +03:00
|
|
|
if err != nil {
|
2023-10-27 01:54:08 +03:00
|
|
|
return dnsInfo, errors.Wrapf(err, "Error executing interface status with cmd %s", cmd)
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("console output for above cmd", zap.Any("out", out))
|
2019-05-01 02:27:48 +03:00
|
|
|
|
|
|
|
lineArr := strings.Split(out, lineDelimiter)
|
|
|
|
if len(lineArr) <= 0 {
|
2023-09-16 03:14:44 +03:00
|
|
|
return dnsInfo, fmt.Errorf("Console output doesn't have any lines") //nolint
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
|
2021-01-28 22:59:27 +03:00
|
|
|
dnsServerFound := false
|
2019-05-01 02:27:48 +03:00
|
|
|
for _, line := range lineArr {
|
|
|
|
if strings.Contains(line, dnsServersStr) {
|
|
|
|
dnsServerSplit := strings.Split(line, colonDelimiter)
|
|
|
|
if len(dnsServerSplit) > 1 {
|
2021-01-28 22:59:27 +03:00
|
|
|
dnsServerFound = true
|
2019-05-03 01:51:40 +03:00
|
|
|
dnsServerSplit[1] = strings.TrimSpace(dnsServerSplit[1])
|
|
|
|
dnsInfo.Servers = append(dnsInfo.Servers, dnsServerSplit[1])
|
|
|
|
}
|
2021-01-28 22:59:27 +03:00
|
|
|
} else if !strings.Contains(line, colonDelimiter) && dnsServerFound {
|
|
|
|
dnsServer := strings.TrimSpace(line)
|
|
|
|
dnsInfo.Servers = append(dnsInfo.Servers, dnsServer)
|
|
|
|
} else {
|
|
|
|
dnsServerFound = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, line := range lineArr {
|
|
|
|
if strings.Contains(line, dnsDomainStr) {
|
2019-05-03 01:51:40 +03:00
|
|
|
dnsDomainSplit := strings.Split(line, colonDelimiter)
|
|
|
|
if len(dnsDomainSplit) > 1 {
|
|
|
|
dnsInfo.Suffix = strings.TrimSpace(dnsDomainSplit[1])
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-03 01:51:40 +03:00
|
|
|
return dnsInfo, nil
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
|
2023-10-13 22:08:58 +03:00
|
|
|
func (nm *networkManager) saveDNSConfig(extIf *externalInterface) error {
|
|
|
|
dnsInfo, err := nm.readDNSInfo(extIf.Name)
|
2019-05-03 01:51:40 +03:00
|
|
|
if err != nil || len(dnsInfo.Servers) == 0 || dnsInfo.Suffix == "" {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Failed to read dns info from interface", zap.Any("dnsInfo", dnsInfo), zap.String("extIfName", extIf.Name),
|
|
|
|
zap.Error(err))
|
2019-05-03 01:51:40 +03:00
|
|
|
return err
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
|
2019-05-03 01:51:40 +03:00
|
|
|
extIf.DNSInfo = dnsInfo
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Saved DNS Info", zap.Any("DNSInfo", extIf.DNSInfo), zap.String("extIfName", extIf.Name))
|
2019-05-03 01:51:40 +03:00
|
|
|
|
|
|
|
return nil
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
|
2017-03-02 03:59:37 +03:00
|
|
|
// 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 {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Adding IP address to interface", zap.Any("addr", addr), zap.String("Name", targetIf.Name))
|
2017-03-02 03:59:37 +03:00
|
|
|
|
2021-09-20 21:57:12 +03:00
|
|
|
err := nm.netlink.AddIPAddress(targetIf.Name, addr.IP, addr)
|
2018-02-22 22:22:47 +03:00
|
|
|
if err != nil && !strings.Contains(strings.ToLower(err.Error()), "file exists") {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Failed to add IP address", zap.Any("addr", addr), zap.Error(err))
|
2017-03-02 03:59:37 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add IP routes.
|
|
|
|
for _, route := range extIf.Routes {
|
|
|
|
route.LinkIndex = targetIf.Index
|
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Adding IP route", zap.Any("route", route))
|
2017-03-02 03:59:37 +03:00
|
|
|
|
2021-09-20 21:57:12 +03:00
|
|
|
err := nm.netlink.AddIPRoute((*netlink.Route)(route))
|
2017-03-02 03:59:37 +03:00
|
|
|
if err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Failed to add IP route", zap.Any("route", route), zap.Error(err))
|
2017-03-02 03:59:37 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-10-13 22:08:58 +03:00
|
|
|
func (nm *networkManager) applyDNSConfig(extIf *externalInterface, ifName string) error {
|
2021-01-28 22:59:27 +03:00
|
|
|
var (
|
2023-10-27 01:54:08 +03:00
|
|
|
setDNSList []string
|
|
|
|
cmd string
|
2021-01-28 22:59:27 +03:00
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if extIf != nil {
|
|
|
|
for _, server := range extIf.DNSInfo.Servers {
|
|
|
|
if net.ParseIP(server).To4() == nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Invalid dns ip", zap.String("server", server))
|
2021-01-28 22:59:27 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-10-27 01:54:08 +03:00
|
|
|
setDNSList = append(setDNSList, server)
|
2021-01-28 22:59:27 +03:00
|
|
|
}
|
2019-09-21 02:15:08 +03:00
|
|
|
|
2023-10-27 01:54:08 +03:00
|
|
|
if len(setDNSList) > 0 {
|
|
|
|
cmd, err = nm.addDNSServers(ifName, setDNSList)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "Error generating add DNS Servers cmd")
|
|
|
|
}
|
|
|
|
|
2023-10-13 22:08:58 +03:00
|
|
|
_, err = nm.plClient.ExecuteCommand(cmd)
|
2021-04-13 21:00:49 +03:00
|
|
|
if err != nil {
|
2023-10-27 01:54:08 +03:00
|
|
|
return errors.Wrapf(err, "Error executing add DNS Servers with cmd %s", cmd)
|
2021-04-13 21:00:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if extIf.DNSInfo.Suffix != "" {
|
2023-10-27 01:54:08 +03:00
|
|
|
cmd, err = nm.addDomain(ifName, extIf.DNSInfo.Suffix)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "Error generating add domain cmd")
|
|
|
|
}
|
|
|
|
|
2023-10-13 22:08:58 +03:00
|
|
|
_, err = nm.plClient.ExecuteCommand(cmd)
|
2023-10-27 01:54:08 +03:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrapf(err, "Error executing add Domain with cmd %s", cmd)
|
|
|
|
}
|
2019-09-21 02:15:08 +03:00
|
|
|
}
|
|
|
|
|
2019-05-03 01:51:40 +03:00
|
|
|
}
|
|
|
|
|
2019-05-01 02:27:48 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
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 {
|
2020-04-16 08:30:48 +03:00
|
|
|
var (
|
|
|
|
err error
|
|
|
|
networkClient NetworkClient
|
|
|
|
)
|
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Connecting interface", zap.String("Name", extIf.Name))
|
|
|
|
defer func() {
|
|
|
|
logger.Info("Connecting interface completed", zap.String("Name", extIf.Name), zap.Error(err))
|
|
|
|
}()
|
2017-03-02 03:59:37 +03:00
|
|
|
// Check whether this interface is already connected.
|
|
|
|
if extIf.BridgeName != "" {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Interface is already connected to bridge", zap.String("BridgeName", extIf.BridgeName))
|
2017-03-02 03:59:37 +03:00
|
|
|
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
|
|
|
|
2018-07-06 21:45:47 +03:00
|
|
|
opt, _ := nwInfo.Options[genericData].(map[string]interface{})
|
|
|
|
if opt != nil && opt[VlanIDKey] != nil {
|
2021-10-16 00:28:37 +03:00
|
|
|
networkClient = NewOVSClient(bridgeName, extIf.Name, ovsctl.NewOvsctl(), nm.netlink, nm.plClient)
|
2018-07-06 21:45:47 +03:00
|
|
|
} else {
|
2021-10-16 00:28:37 +03:00
|
|
|
networkClient = NewLinuxBridgeClient(bridgeName, extIf.Name, *nwInfo, nm.netlink, nm.plClient)
|
2018-07-06 21:45:47 +03:00
|
|
|
}
|
|
|
|
|
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)
|
2020-11-17 01:24:00 +03:00
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
if err != nil {
|
2016-11-22 23:19:54 +03:00
|
|
|
// Create the bridge.
|
2020-04-16 08:30:48 +03:00
|
|
|
if err = networkClient.CreateBridge(); err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Error while creating bridge", zap.Error(err))
|
2016-09-22 01:39:25 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bridge, err = net.InterfaceByName(bridgeName)
|
|
|
|
if err != nil {
|
2017-08-17 00:13:46 +03:00
|
|
|
return err
|
2016-09-22 01:39:25 +03:00
|
|
|
}
|
2016-11-22 23:19:54 +03:00
|
|
|
} else {
|
|
|
|
// Use the existing bridge.
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Found existing bridge", zap.String("bridgeName", bridgeName))
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2020-04-16 08:30:48 +03:00
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("cleanup network")
|
2020-04-16 08:30:48 +03:00
|
|
|
nm.disconnectExternalInterface(extIf, networkClient)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
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 {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Failed to save IP configuration for interface",
|
|
|
|
zap.String("Name", hostIf.Name), zap.Error(err))
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2021-01-28 22:59:27 +03:00
|
|
|
/*
|
|
|
|
If custom dns server is updated, VM needs reboot for the change to take effect.
|
|
|
|
*/
|
2019-05-01 02:27:48 +03:00
|
|
|
isGreaterOrEqualUbuntu17 := isGreaterOrEqaulUbuntuVersion(ubuntuVersion17)
|
2021-08-25 01:38:22 +03:00
|
|
|
isSystemdResolvedActive := false
|
2019-05-01 02:27:48 +03:00
|
|
|
if isGreaterOrEqualUbuntu17 {
|
2021-08-25 01:38:22 +03:00
|
|
|
// Don't copy dns servers if systemd-resolved isn't available
|
2023-10-13 22:08:58 +03:00
|
|
|
if _, cmderr := nm.plClient.ExecuteCommand("systemctl status systemd-resolved"); cmderr == nil {
|
2021-08-25 01:38:22 +03:00
|
|
|
isSystemdResolvedActive = true
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Saving dns config from", zap.String("Name", hostIf.Name))
|
2023-10-13 22:08:58 +03:00
|
|
|
if err = nm.saveDNSConfig(extIf); err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Failed to save dns config", zap.Error(err))
|
2021-08-25 01:38:22 +03:00
|
|
|
return err
|
|
|
|
}
|
2019-05-03 01:51:40 +03:00
|
|
|
}
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// External interface down.
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Setting link state down", zap.String("Name", hostIf.Name))
|
2021-09-20 21:57:12 +03:00
|
|
|
err = nm.netlink.SetLinkState(hostIf.Name, false)
|
2016-09-22 01:39:25 +03:00
|
|
|
if err != nil {
|
2017-08-17 00:13:46 +03:00
|
|
|
return err
|
2016-09-22 01:39:25 +03:00
|
|
|
}
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// Connect the external interface to the bridge.
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Setting link master", zap.String("Name", hostIf.Name), zap.String("bridgeName", bridgeName))
|
2020-04-16 08:30:48 +03:00
|
|
|
if err = networkClient.SetBridgeMasterToHostInterface(); err != nil {
|
2017-08-17 00:13:46 +03:00
|
|
|
return err
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
// External interface up.
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Setting link state up", zap.String("Name", hostIf.Name))
|
2021-09-20 21:57:12 +03:00
|
|
|
err = nm.netlink.SetLinkState(hostIf.Name, true)
|
2016-06-03 05:02:07 +03:00
|
|
|
if err != nil {
|
2017-08-17 00:13:46 +03:00
|
|
|
return err
|
2016-09-22 01:39:25 +03:00
|
|
|
}
|
|
|
|
|
2018-07-06 21:45:47 +03:00
|
|
|
// Bridge up.
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Setting link state up", zap.String("bridgeName", bridgeName))
|
2021-09-20 21:57:12 +03:00
|
|
|
err = nm.netlink.SetLinkState(bridgeName, true)
|
2017-03-31 16:45:28 +03:00
|
|
|
if err != nil {
|
2017-08-17 00:13:46 +03:00
|
|
|
return err
|
2017-03-31 16:45:28 +03:00
|
|
|
}
|
|
|
|
|
2018-07-06 21:45:47 +03:00
|
|
|
// Add the bridge rules.
|
|
|
|
err = networkClient.AddL2Rules(extIf)
|
2016-09-22 01:39:25 +03:00
|
|
|
if err != nil {
|
2017-08-17 00:13:46 +03:00
|
|
|
return err
|
2016-09-22 01:39:25 +03:00
|
|
|
}
|
|
|
|
|
2018-07-06 21:45:47 +03:00
|
|
|
// External interface hairpin on.
|
2020-01-17 01:27:25 +03:00
|
|
|
if !nwInfo.DisableHairpinOnHostInterface {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Setting link hairpin on", zap.String("Name", hostIf.Name))
|
2020-04-16 08:30:48 +03:00
|
|
|
if err = networkClient.SetHairpinOnHostInterface(true); err != nil {
|
2020-01-17 01:27:25 +03:00
|
|
|
return err
|
|
|
|
}
|
2018-07-06 21:45:47 +03:00
|
|
|
}
|
|
|
|
|
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 {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Failed to apply interface IP configuration", zap.Error(err))
|
2019-05-01 02:27:48 +03:00
|
|
|
return err
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2021-08-25 01:38:22 +03:00
|
|
|
if isGreaterOrEqualUbuntu17 && isSystemdResolvedActive {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Applying dns config on", zap.String("bridgeName", bridgeName))
|
2019-05-01 02:27:48 +03:00
|
|
|
|
2023-10-13 22:08:58 +03:00
|
|
|
if err = nm.applyDNSConfig(extIf, bridgeName); err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Failed to apply DNS configuration with", zap.Error(err))
|
2019-05-01 02:27:48 +03:00
|
|
|
return err
|
|
|
|
}
|
2016-09-22 01:39:25 +03:00
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Applied dns config on", zap.Any("DNSInfo", extIf.DNSInfo), zap.String("bridgeName", bridgeName))
|
2019-05-01 02:27:48 +03:00
|
|
|
}
|
|
|
|
|
2020-04-16 08:30:48 +03:00
|
|
|
if nwInfo.IPV6Mode == IPV6Nat {
|
|
|
|
// adds pod cidr gateway ip to bridge
|
2021-10-16 00:28:37 +03:00
|
|
|
if err = nm.addIpv6NatGateway(nwInfo); err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Adding IPv6 Nat Gateway failed with", zap.Error(err))
|
2020-04-16 08:30:48 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-10-16 00:28:37 +03:00
|
|
|
if err = nm.addIpv6SnatRule(extIf, nwInfo); err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Adding IPv6 Snat Rule failed with", zap.Error(err))
|
2020-04-16 08:30:48 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// unmark packet if set by kube-proxy to skip kube-postrouting rule and processed
|
|
|
|
// by cni snat rule
|
|
|
|
if err = iptables.InsertIptableRule(iptables.V6, iptables.Mangle, iptables.Postrouting, "", "MARK --set-mark 0x0"); err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Adding Iptable mangle rule failed", zap.Error(err))
|
2020-04-16 08:30:48 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-01 02:27:48 +03:00
|
|
|
extIf.BridgeName = bridgeName
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Connected interface to bridge", zap.String("Name", extIf.Name), zap.String("BridgeName", 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
|
|
|
// DisconnectExternalInterface disconnects a host interface from its bridge.
|
2018-07-06 21:45:47 +03:00
|
|
|
func (nm *networkManager) disconnectExternalInterface(extIf *externalInterface, networkClient NetworkClient) {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Disconnecting interface", zap.String("Name", extIf.Name))
|
2016-09-22 01:39:25 +03:00
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Deleting bridge rules")
|
2017-03-07 03:24:20 +03:00
|
|
|
// Delete bridge rules set on the external interface.
|
2018-07-06 21:45:47 +03:00
|
|
|
networkClient.DeleteL2Rules(extIf)
|
2016-09-22 01:39:25 +03:00
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Deleting bridge")
|
2018-07-06 21:45:47 +03:00
|
|
|
// Delete Bridge
|
|
|
|
networkClient.DeleteBridge()
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
extIf.BridgeName = ""
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Restoring ipconfig with primary interface", zap.String("Name", extIf.Name))
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2017-03-02 03:59:37 +03:00
|
|
|
// Restore IP configuration.
|
|
|
|
hostIf, _ := net.InterfaceByName(extIf.Name)
|
2018-07-06 21:45:47 +03:00
|
|
|
err := nm.applyIPConfig(extIf, hostIf)
|
2017-03-02 03:59:37 +03:00
|
|
|
if err != nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("Failed to apply IP configuration", zap.Error(err))
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
|
|
|
|
2016-09-22 01:39:25 +03:00
|
|
|
extIf.IPAddresses = nil
|
|
|
|
extIf.Routes = nil
|
|
|
|
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Disconnected interface", zap.String("Name", extIf.Name))
|
2018-07-06 21:45:47 +03:00
|
|
|
}
|
2016-06-03 05:02:07 +03:00
|
|
|
|
2021-09-20 21:57:12 +03:00
|
|
|
func (*networkManager) addToIptables(cmds []iptables.IPTableEntry) error {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Adding additional iptable rules...")
|
2020-11-17 01:24:00 +03:00
|
|
|
for _, cmd := range cmds {
|
|
|
|
err := iptables.RunCmd(cmd.Version, cmd.Params)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Successfully run iptables rule", zap.Any("cmd", cmd))
|
2020-11-17 01:24:00 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-16 08:30:48 +03:00
|
|
|
// Add ipv6 nat gateway IP on bridge
|
2021-10-16 00:28:37 +03:00
|
|
|
func (nm *networkManager) addIpv6NatGateway(nwInfo *NetworkInfo) error {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Adding ipv6 nat gateway on azure bridge")
|
2020-04-16 08:30:48 +03:00
|
|
|
for _, subnetInfo := range nwInfo.Subnets {
|
|
|
|
if subnetInfo.Family == platform.AfINET6 {
|
|
|
|
ipAddr := []net.IPNet{{
|
|
|
|
IP: subnetInfo.Gateway,
|
|
|
|
Mask: subnetInfo.Prefix.Mask,
|
|
|
|
}}
|
2021-10-16 00:28:37 +03:00
|
|
|
nuc := networkutils.NewNetworkUtils(nm.netlink, nm.plClient)
|
|
|
|
err := nuc.AssignIPToInterface(nwInfo.BridgeName, ipAddr)
|
2021-09-20 21:57:12 +03:00
|
|
|
if err != nil {
|
|
|
|
return newErrorNetworkManager(err.Error())
|
|
|
|
}
|
2020-04-16 08:30:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// snat ipv6 traffic to secondary ipv6 ip before leaving VM
|
2021-10-16 00:28:37 +03:00
|
|
|
func (*networkManager) addIpv6SnatRule(extIf *externalInterface, nwInfo *NetworkInfo) error {
|
|
|
|
var (
|
|
|
|
ipv6SnatRuleSet bool
|
|
|
|
ipv6SubnetPrefix net.IPNet
|
|
|
|
)
|
|
|
|
|
|
|
|
for _, subnet := range nwInfo.Subnets {
|
|
|
|
if subnet.Family == platform.AfINET6 {
|
|
|
|
ipv6SubnetPrefix = subnet.Prefix
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ipv6SubnetPrefix.IP) == 0 {
|
|
|
|
return errSubnetV6NotFound
|
|
|
|
}
|
|
|
|
|
2020-04-16 08:30:48 +03:00
|
|
|
for _, ipAddr := range extIf.IPAddresses {
|
|
|
|
if ipAddr.IP.To4() == nil {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Adding ipv6 snat rule")
|
2021-10-16 00:28:37 +03:00
|
|
|
matchSrcPrefix := fmt.Sprintf("-s %s", ipv6SubnetPrefix.String())
|
|
|
|
if err := networkutils.AddSnatRule(matchSrcPrefix, ipAddr.IP); err != nil {
|
|
|
|
return fmt.Errorf("Adding iptable snat rule failed:%w", err)
|
2020-04-16 08:30:48 +03:00
|
|
|
}
|
2021-10-16 00:28:37 +03:00
|
|
|
ipv6SnatRuleSet = true
|
2020-04-16 08:30:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-16 00:28:37 +03:00
|
|
|
if !ipv6SnatRuleSet {
|
|
|
|
return errV6SnatRuleNotSet
|
|
|
|
}
|
|
|
|
|
2020-04-16 08:30:48 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-07-06 21:45:47 +03:00
|
|
|
func getNetworkInfoImpl(nwInfo *NetworkInfo, nw *network) {
|
|
|
|
if nw.VlanId != 0 {
|
|
|
|
vlanMap := make(map[string]interface{})
|
|
|
|
vlanMap[VlanIDKey] = strconv.Itoa(nw.VlanId)
|
|
|
|
nwInfo.Options[genericData] = vlanMap
|
|
|
|
}
|
2016-06-03 05:02:07 +03:00
|
|
|
}
|
2018-08-19 00:50:49 +03:00
|
|
|
|
2021-09-20 21:57:12 +03:00
|
|
|
// AddStaticRoute adds a static route to the interface.
|
2021-10-16 00:28:37 +03:00
|
|
|
func AddStaticRoute(nl netlink.NetlinkInterface, netioshim netio.NetIOInterface, ip, interfaceName string) error {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Info("Adding static route", zap.String("ip", ip))
|
2018-08-19 00:50:49 +03:00
|
|
|
var routes []RouteInfo
|
|
|
|
_, ipNet, _ := net.ParseCIDR(ip)
|
|
|
|
gwIP := net.ParseIP("0.0.0.0")
|
|
|
|
route := RouteInfo{Dst: *ipNet, Gw: gwIP}
|
|
|
|
routes = append(routes, route)
|
2021-10-16 00:28:37 +03:00
|
|
|
if err := addRoutes(nl, netioshim, interfaceName, routes); err != nil {
|
2018-08-19 00:50:49 +03:00
|
|
|
if err != nil && !strings.Contains(strings.ToLower(err.Error()), "file exists") {
|
2023-09-16 03:14:44 +03:00
|
|
|
logger.Error("addroutes failed with error", zap.Error(err))
|
2018-08-19 00:50:49 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|