467 строки
10 KiB
Go
467 строки
10 KiB
Go
// Copyright 2017 Microsoft. All rights reserved.
|
|
// MIT License
|
|
|
|
//go:build linux
|
|
// +build linux
|
|
|
|
package netlink
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/Azure/azure-container-networking/log"
|
|
"github.com/pkg/errors"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// Link types.
|
|
const (
|
|
LINK_TYPE_BRIDGE = "bridge"
|
|
LINK_TYPE_VETH = "veth"
|
|
LINK_TYPE_IPVLAN = "ipvlan"
|
|
LINK_TYPE_DUMMY = "dummy"
|
|
)
|
|
|
|
// IPVLAN link attributes.
|
|
type IPVlanMode uint16
|
|
|
|
const (
|
|
IPVLAN_MODE_L2 IPVlanMode = iota
|
|
IPVLAN_MODE_L3
|
|
IPVLAN_MODE_L3S
|
|
IPVLAN_MODE_MAX
|
|
)
|
|
|
|
const (
|
|
ADD = iota
|
|
REMOVE
|
|
)
|
|
|
|
// Link represents a network interface.
|
|
type Link interface {
|
|
Info() *LinkInfo
|
|
}
|
|
|
|
// LinkInfo respresents the common properties of all network interfaces.
|
|
type LinkInfo struct {
|
|
Type string
|
|
Name string
|
|
Flags net.Flags
|
|
MTU uint
|
|
TxQLen uint
|
|
ParentIndex int
|
|
MacAddress net.HardwareAddr
|
|
IPAddr net.IP
|
|
}
|
|
|
|
func (linkInfo *LinkInfo) Info() *LinkInfo {
|
|
return linkInfo
|
|
}
|
|
|
|
// BridgeLink represents an ethernet bridge.
|
|
type BridgeLink struct {
|
|
LinkInfo
|
|
}
|
|
|
|
// VEthLink represents a virtual ethernet network interface.
|
|
type VEthLink struct {
|
|
LinkInfo
|
|
PeerName string
|
|
}
|
|
|
|
// IPVlanLink represents an IPVlan network interface.
|
|
type IPVlanLink struct {
|
|
LinkInfo
|
|
Mode IPVlanMode
|
|
}
|
|
|
|
// DummyLink represents a dummy network interface.
|
|
type DummyLink struct {
|
|
LinkInfo
|
|
}
|
|
|
|
// AddLink adds a new network interface of a specified type.
|
|
func (Netlink) AddLink(link Link) error {
|
|
info := link.Info()
|
|
|
|
if info.Name == "" || info.Type == "" {
|
|
return fmt.Errorf("Invalid link name or type")
|
|
}
|
|
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_NEWLINK, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
|
|
|
|
// Set interface information.
|
|
ifInfo := newIfInfoMsg()
|
|
|
|
// Set interface flags.
|
|
if info.Flags&net.FlagUp != 0 {
|
|
ifInfo.Change = unix.IFF_UP
|
|
ifInfo.Flags = unix.IFF_UP
|
|
}
|
|
req.addPayload(ifInfo)
|
|
|
|
// Set interface name.
|
|
attrIfName := newAttributeStringZ(unix.IFLA_IFNAME, info.Name)
|
|
req.addPayload(attrIfName)
|
|
|
|
// Set MTU.
|
|
if info.MTU > 0 {
|
|
req.addPayload(newAttributeUint32(unix.IFLA_MTU, uint32(info.MTU)))
|
|
}
|
|
|
|
// Set transmission queue length.
|
|
if info.TxQLen > 0 {
|
|
req.addPayload(newAttributeUint32(unix.IFLA_TXQLEN, uint32(info.TxQLen)))
|
|
}
|
|
|
|
// Set parent interface index.
|
|
if info.ParentIndex != 0 {
|
|
req.addPayload(newAttributeUint32(unix.IFLA_LINK, uint32(info.ParentIndex)))
|
|
}
|
|
|
|
// Set the mac address on the interface
|
|
if info.MacAddress != nil {
|
|
req.addPayload(newRtAttr(unix.IFLA_ADDRESS, []byte(info.MacAddress)))
|
|
}
|
|
|
|
// Set link info.
|
|
attrLinkInfo := newAttribute(unix.IFLA_LINKINFO, nil)
|
|
attrLinkInfo.addNested(newAttributeString(IFLA_INFO_KIND, info.Type))
|
|
|
|
// Set link type-specific attributes.
|
|
if veth, ok := link.(*VEthLink); ok {
|
|
// Set VEth attributes.
|
|
attrData := newAttribute(IFLA_INFO_DATA, nil)
|
|
|
|
attrPeer := newAttribute(VETH_INFO_PEER, nil)
|
|
attrPeer.addNested(newIfInfoMsg())
|
|
attrPeer.addNested(newAttributeStringZ(unix.IFLA_IFNAME, veth.PeerName))
|
|
attrData.addNested(attrPeer)
|
|
|
|
attrLinkInfo.addNested(attrData)
|
|
|
|
} else if ipvlan, ok := link.(*IPVlanLink); ok {
|
|
// Set IPVlan attributes.
|
|
attrData := newAttribute(IFLA_INFO_DATA, nil)
|
|
attrData.addNested(newAttributeUint16(IFLA_IPVLAN_MODE, uint16(ipvlan.Mode)))
|
|
|
|
attrLinkInfo.addNested(attrData)
|
|
}
|
|
|
|
req.addPayload(attrLinkInfo)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
func (Netlink) SetLinkMTU(name string, mtu int) error {
|
|
iface, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
log.Printf("[net] Interface not found. returning error")
|
|
return errors.Wrap(err, "SetLinkMTU:InterfaceByName failed")
|
|
}
|
|
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Index = int32(iface.Index)
|
|
req.addPayload(ifInfo)
|
|
|
|
mtuData := newAttributeUint32(unix.IFLA_MTU, uint32(mtu))
|
|
req.addPayload(mtuData)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// DeleteLink deletes a network interface.
|
|
func (Netlink) DeleteLink(name string) error {
|
|
if name == "" {
|
|
log.Printf("[net] Invalid link name. Not returning error")
|
|
return nil
|
|
}
|
|
|
|
iface, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
log.Printf("[net] Interface not found. Not returning error")
|
|
return nil
|
|
}
|
|
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_DELLINK, unix.NLM_F_ACK)
|
|
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Index = int32(iface.Index)
|
|
req.addPayload(ifInfo)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// SetLinkName sets the name of a network interface.
|
|
func (Netlink) SetLinkName(name string, newName string) error {
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iface, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
|
|
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Type = unix.RTM_SETLINK
|
|
ifInfo.Index = int32(iface.Index)
|
|
ifInfo.Flags = unix.NLM_F_REQUEST
|
|
ifInfo.Change = DEFAULT_CHANGE
|
|
req.addPayload(ifInfo)
|
|
|
|
attrName := newAttributeString(unix.IFLA_IFNAME, newName)
|
|
req.addPayload(attrName)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// SetLinkState sets the operational state of a network interface.
|
|
func (Netlink) SetLinkState(name string, up bool) error {
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iface, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_NEWLINK, unix.NLM_F_ACK)
|
|
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Type = unix.RTM_SETLINK
|
|
ifInfo.Index = int32(iface.Index)
|
|
|
|
if up {
|
|
ifInfo.Flags = unix.IFF_UP
|
|
ifInfo.Change = unix.IFF_UP
|
|
} else {
|
|
ifInfo.Flags = 0 & ^unix.IFF_UP
|
|
ifInfo.Change = DEFAULT_CHANGE
|
|
}
|
|
|
|
req.addPayload(ifInfo)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// SetLinkMaster sets the master (upper) device of a network interface.
|
|
func (Netlink) SetLinkMaster(name string, master string) error {
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iface, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var masterIndex uint32
|
|
if master != "" {
|
|
masterIface, err := net.InterfaceByName(master)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
masterIndex = uint32(masterIface.Index)
|
|
}
|
|
|
|
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
|
|
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Type = unix.RTM_SETLINK
|
|
ifInfo.Index = int32(iface.Index)
|
|
ifInfo.Flags = unix.NLM_F_REQUEST
|
|
ifInfo.Change = DEFAULT_CHANGE
|
|
req.addPayload(ifInfo)
|
|
|
|
attrMaster := newAttributeUint32(unix.IFLA_MASTER, masterIndex)
|
|
req.addPayload(attrMaster)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// SetLinkNetNs sets the network namespace of a network interface.
|
|
func (Netlink) SetLinkNetNs(name string, fd uintptr) error {
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iface, err := net.InterfaceByName(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
|
|
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Type = unix.RTM_SETLINK
|
|
ifInfo.Index = int32(iface.Index)
|
|
ifInfo.Flags = unix.NLM_F_REQUEST
|
|
ifInfo.Change = DEFAULT_CHANGE
|
|
req.addPayload(ifInfo)
|
|
|
|
attrNetNs := newAttributeUint32(IFLA_NET_NS_FD, uint32(fd))
|
|
req.addPayload(attrNetNs)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// SetLinkAddress sets the link layer hardware address of a network interface.
|
|
func (Netlink) SetLinkAddress(ifName string, hwAddress net.HardwareAddr) error {
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iface, err := net.InterfaceByName(ifName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
|
|
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Type = unix.RTM_SETLINK
|
|
ifInfo.Index = int32(iface.Index)
|
|
ifInfo.Flags = unix.NLM_F_REQUEST
|
|
ifInfo.Change = DEFAULT_CHANGE
|
|
req.addPayload(ifInfo)
|
|
|
|
req.addPayload(newAttribute(unix.IFLA_ADDRESS, hwAddress))
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// SetLinkPromisc sets the promiscuous mode of a network interface.
|
|
// TODO do we need this function, not used anywhere currently
|
|
func (Netlink) SetLinkPromisc(ifName string, on bool) error {
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iface, err := net.InterfaceByName(ifName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
|
|
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Type = unix.RTM_SETLINK
|
|
ifInfo.Index = int32(iface.Index)
|
|
|
|
if on {
|
|
ifInfo.Flags = unix.IFF_PROMISC
|
|
ifInfo.Change = unix.IFF_PROMISC
|
|
} else {
|
|
ifInfo.Flags = 0 & ^unix.IFF_PROMISC
|
|
ifInfo.Change = unix.IFF_PROMISC
|
|
}
|
|
|
|
req.addPayload(ifInfo)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// SetLinkHairpin sets the hairpin (reflective relay) mode of a bridged interface.
|
|
func (Netlink) SetLinkHairpin(bridgeName string, on bool) error {
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
iface, err := net.InterfaceByName(bridgeName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK)
|
|
|
|
ifInfo := newIfInfoMsg()
|
|
ifInfo.Family = unix.AF_BRIDGE
|
|
ifInfo.Type = unix.RTM_SETLINK
|
|
ifInfo.Index = int32(iface.Index)
|
|
ifInfo.Flags = unix.NLM_F_REQUEST
|
|
ifInfo.Change = DEFAULT_CHANGE
|
|
req.addPayload(ifInfo)
|
|
|
|
hairpin := []byte{0}
|
|
if on {
|
|
hairpin[0] = byte(1)
|
|
}
|
|
|
|
attrProtInfo := newAttribute(unix.IFLA_PROTINFO|unix.NLA_F_NESTED, nil)
|
|
attrProtInfo.addNested(newAttribute(IFLA_BRPORT_MODE, hairpin))
|
|
req.addPayload(attrProtInfo)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|
|
|
|
// SetOrRemoveLinkAddress sets/removes static arp entry based on mode
|
|
func (Netlink) SetOrRemoveLinkAddress(linkInfo LinkInfo, mode, linkState int) error {
|
|
s, err := getSocket()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var req *message
|
|
state := 0
|
|
if mode == ADD {
|
|
req = newRequest(unix.RTM_NEWNEIGH, unix.NLM_F_CREATE|unix.NLM_F_REPLACE|unix.NLM_F_ACK)
|
|
} else {
|
|
req = newRequest(unix.RTM_DELNEIGH, unix.NLM_F_ACK)
|
|
}
|
|
state = linkState
|
|
|
|
iface, err := net.InterfaceByName(linkInfo.Name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
msg := neighMsg{
|
|
Family: uint8(GetIPAddressFamily(linkInfo.IPAddr)),
|
|
Index: uint32(iface.Index),
|
|
State: uint16(state),
|
|
}
|
|
|
|
req.addPayload(&msg)
|
|
|
|
ipData := linkInfo.IPAddr.To4()
|
|
if ipData == nil {
|
|
ipData = linkInfo.IPAddr.To16()
|
|
}
|
|
|
|
dstData := newRtAttr(NDA_DST, ipData)
|
|
req.addPayload(dstData)
|
|
|
|
hwData := newRtAttr(NDA_LLADDR, []byte(linkInfo.MacAddress))
|
|
req.addPayload(hwData)
|
|
|
|
return s.sendAndWaitForAck(req)
|
|
}
|