azure-container-networking/netlink/link_linux.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)
}