zoreide/vendor/github.com/milosgajdos/tenus/link_linux.go

334 lines
10 KiB
Go
Raw Normal View History

2023-08-09 09:08:05 -05:00
package tenus
import (
"fmt"
"net"
"os"
"syscall"
"github.com/docker/libcontainer/netlink"
"github.com/docker/libcontainer/system"
)
// LinkOptions allows you to specify network link options.
type LinkOptions struct {
// MAC address
MacAddr string
// Maximum Transmission Unit
MTU int
// Link network flags i.e. FlagUp, FlagLoopback, FlagMulticast
Flags net.Flags
// Network namespace in which the network link should be created
Ns int
}
// Linker is a generic Linux network link
type Linker interface {
// NetInterface returns the link's logical network interface
NetInterface() *net.Interface
// DeleteLink deletes the link from Linux host
DeleteLink() error
// SetLinkMTU sets the link's MTU.
SetLinkMTU(int) error
// SetLinkMacAddress sets the link's MAC address.
SetLinkMacAddress(string) error
// SetLinkUp brings the link up
SetLinkUp() error
// SetLinkDown brings the link down
SetLinkDown() error
// SetLinkIp configures the link's IP address
SetLinkIp(net.IP, *net.IPNet) error
// UnsetLinkIp remove and IP address from the link
UnsetLinkIp(net.IP, *net.IPNet) error
// SetLinkDefaultGw configures the link's default gateway
SetLinkDefaultGw(*net.IP) error
// SetLinkNetNsPid moves the link to network namespace specified by PID
SetLinkNetNsPid(int) error
// SetLinkNetInNs configures network settings of the link in network namespace
SetLinkNetInNs(int, net.IP, *net.IPNet, *net.IP) error
}
// Link has a logical network interface
type Link struct {
ifc *net.Interface
}
// NewLink creates new network link on Linux host.
//
// It is equivalent of running: ip link add name ${ifcName} type dummy
// NewLink returns Linker which is initialized to a pointer of type Link if the
// link was created successfully on the Linux host.
// It returns error if the network link could not be created on Linux host.
func NewLink(ifcName string) (Linker, error) {
if ok, err := NetInterfaceNameValid(ifcName); !ok {
return nil, err
}
if _, err := net.InterfaceByName(ifcName); err == nil {
return nil, fmt.Errorf("Interface name %s already assigned on the host", ifcName)
}
if err := netlink.NetworkLinkAdd(ifcName, "dummy"); err != nil {
return nil, fmt.Errorf("Could not create new link %s: %s", ifcName, err)
}
newIfc, err := net.InterfaceByName(ifcName)
if err != nil {
return nil, fmt.Errorf("Could not find the new interface: %s", err)
}
return &Link{
ifc: newIfc,
}, nil
}
// NewLinkFrom creates new tenus link on Linux host from an existing interface of given name
func NewLinkFrom(ifcName string) (Linker, error) {
if ok, err := NetInterfaceNameValid(ifcName); !ok {
return nil, err
}
newIfc, err := net.InterfaceByName(ifcName)
if err != nil {
return nil, fmt.Errorf("Could not find the new interface: %s", err)
}
return &Link{
ifc: newIfc,
}, nil
}
// NewLinkWithOptions creates new network link on Linux host and sets some of its network
// parameters passed in as LinkOptions
//
// Calling NewLinkWithOptions is equivalent of running following commands one after another if
// particular option is passed in as a parameter:
// ip link add name ${ifcName} type dummy
// ip link set dev ${ifcName} address ${MAC address}
// ip link set dev ${ifcName} mtu ${MTU value}
// ip link set dev ${ifcName} up
// NewLinkWithOptions returns Linker which is initialized to a pointer of type Link if the network
// link with given LinkOptions was created successfully on the Linux host.
// It attempts to delete the link if any of the LinkOptions are incorrect or if setting the options
// failed and returns error.
func NewLinkWithOptions(ifcName string, opts LinkOptions) (Linker, error) {
if ok, err := NetInterfaceNameValid(ifcName); !ok {
return nil, err
}
if _, err := net.InterfaceByName(ifcName); err == nil {
return nil, fmt.Errorf("Interface name %s already assigned on the host", ifcName)
}
if err := netlink.NetworkLinkAdd(ifcName, "dummy"); err != nil {
return nil, fmt.Errorf("Could not create new link %s: %s", ifcName, err)
}
newIfc, err := net.InterfaceByName(ifcName)
if err != nil {
return nil, fmt.Errorf("Could not find the new interface: %s", err)
}
if (opts != LinkOptions{}) {
errOpts := setLinkOptions(newIfc, opts)
if errOpts != nil {
if errDel := DeleteLink(newIfc.Name); err != nil {
return nil, fmt.Errorf("Incorrect options specified: %s. Attempt to delete the link failed: %s",
errOpts, errDel)
}
return nil, fmt.Errorf("Could not set link options: %s", errOpts)
}
}
return &Link{
ifc: newIfc,
}, nil
}
// DeleteLink deletes netowrk link from Linux Host
// It is equivalent of running: ip link delete dev ${name}
func DeleteLink(name string) error {
return netlink.NetworkLinkDel(name)
}
// NetInterface returns link's logical network interface.
func (l *Link) NetInterface() *net.Interface {
return l.ifc
}
// DeleteLink deletes link interface on Linux host.
// It is equivalent of running: ip link delete dev ${interface name}
func (l *Link) DeleteLink() error {
return netlink.NetworkLinkDel(l.NetInterface().Name)
}
// SetLinkMTU sets link's MTU.
// It is equivalent of running: ip link set dev ${interface name} mtu ${MTU value}
func (l *Link) SetLinkMTU(mtu int) error {
return netlink.NetworkSetMTU(l.NetInterface(), mtu)
}
// SetLinkMacAddress sets link's MAC address.
// It is equivalent of running: ip link set dev ${interface name} address ${address}
func (l *Link) SetLinkMacAddress(macaddr string) error {
return netlink.NetworkSetMacAddress(l.NetInterface(), macaddr)
}
// SetLinkUp brings the link up.
// It is equivalent of running: ip link set dev ${interface name} up
func (l *Link) SetLinkUp() error {
return netlink.NetworkLinkUp(l.NetInterface())
}
// SetLinkDown brings the link down.
// It is equivalent of running: ip link set dev ${interface name} down
func (l *Link) SetLinkDown() error {
return netlink.NetworkLinkDown(l.NetInterface())
}
// SetLinkIp configures the link's IP address.
// It is equivalent of running: ip address add ${address}/${mask} dev ${interface name}
func (l *Link) SetLinkIp(ip net.IP, network *net.IPNet) error {
return netlink.NetworkLinkAddIp(l.NetInterface(), ip, network)
}
// UnsetLinkIp configures the link's IP address.
// It is equivalent of running: ip address del ${address}/${mask} dev ${interface name}
func (l *Link) UnsetLinkIp(ip net.IP, network *net.IPNet) error {
return netlink.NetworkLinkDelIp(l.NetInterface(), ip, network)
}
// SetLinkDefaultGw configures the link's default Gateway.
// It is equivalent of running: ip route add default via ${ip address}
func (l *Link) SetLinkDefaultGw(gw *net.IP) error {
return netlink.AddDefaultGw(gw.String(), l.NetInterface().Name)
}
// SetLinkNetNsPid moves the link to Network namespace specified by PID.
func (l *Link) SetLinkNetNsPid(nspid int) error {
return netlink.NetworkSetNsPid(l.NetInterface(), nspid)
}
// SetLinkNetInNs configures network settings of the link in network namespace specified by PID.
func (l *Link) SetLinkNetInNs(nspid int, ip net.IP, network *net.IPNet, gw *net.IP) error {
origNs, _ := NetNsHandle(os.Getpid())
defer syscall.Close(int(origNs))
defer system.Setns(origNs, syscall.CLONE_NEWNET)
if err := SetNetNsToPid(nspid); err != nil {
return fmt.Errorf("Setting network namespace failed: %s", err)
}
if err := netlink.NetworkLinkAddIp(l.NetInterface(), ip, network); err != nil {
return fmt.Errorf("Unable to set IP: %s in pid: %d network namespace", ip.String(), nspid)
}
if err := netlink.NetworkLinkUp(l.ifc); err != nil {
return fmt.Errorf("Unable to bring %s interface UP: %d", l.ifc.Name, nspid)
}
if gw != nil {
if err := netlink.AddDefaultGw(gw.String(), l.NetInterface().Name); err != nil {
return fmt.Errorf("Unable to set Default gateway: %s in pid: %d network namespace", gw.String(), nspid)
}
}
return nil
}
// SetLinkNsFd sets the link's Linux namespace to the one specified by filesystem path.
func (l *Link) SetLinkNsFd(nspath string) error {
fd, err := syscall.Open(nspath, syscall.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("Could not attach to Network namespace: %s", err)
}
return netlink.NetworkSetNsFd(l.NetInterface(), fd)
}
// SetLinkNsToDocker sets the link's Linux namespace to a running Docker one specified by Docker name.
func (l *Link) SetLinkNsToDocker(name string, dockerHost string) error {
pid, err := DockerPidByName(name, dockerHost)
if err != nil {
return fmt.Errorf("Failed to find docker %s : %s", name, err)
}
return l.SetLinkNetNsPid(pid)
}
// RenameInterfaceByName renames an interface of given name.
func RenameInterfaceByName(old string, newName string) error {
iface, err := net.InterfaceByName(old)
if err != nil {
return err
}
return netlink.NetworkChangeName(iface, newName)
}
// setLinkOptions validates and sets link's various options passed in as LinkOptions.
func setLinkOptions(ifc *net.Interface, opts LinkOptions) error {
macaddr, mtu, flags, ns := opts.MacAddr, opts.MTU, opts.Flags, opts.Ns
// if MTU is passed in LinkOptions
if mtu != 0 {
if err := validMtu(mtu); err != nil {
return err
}
if err := netlink.NetworkSetMTU(ifc, mtu); err != nil {
return fmt.Errorf("Unable to set MTU: %s", err)
}
}
// if MacAddress is passed in LinkOptions
if macaddr != "" {
if err := validMacAddress(macaddr); err != nil {
return err
}
if err := netlink.NetworkSetMacAddress(ifc, macaddr); err != nil {
return fmt.Errorf("Unable to set MAC Address: %s", err)
}
}
// if ns is passed in LinkOptions
if ns != 0 {
if err := validNs(ns); err != nil {
return err
}
if err := netlink.NetworkSetNsPid(ifc, ns); err != nil {
return fmt.Errorf("Unable to set Network namespace: %s", err)
}
}
// if flags is passed in LinkOptions
if flags != 0 {
if err := validFlags(flags); err != nil {
return err
}
if ns != 0 && (ns != 1 || ns != os.Getpid()) {
if (flags & syscall.IFF_UP) == syscall.IFF_UP {
origNs, _ := NetNsHandle(os.Getpid())
defer syscall.Close(int(origNs))
defer system.Setns(origNs, syscall.CLONE_NEWNET)
if err := SetNetNsToPid(ns); err != nil {
return fmt.Errorf("Switching to %d network namespace failed: %s", ns, err)
}
if err := netlink.NetworkLinkUp(ifc); err != nil {
return fmt.Errorf("Unable to bring %s interface UP: %d", ifc.Name, ns)
}
}
} else {
if err := netlink.NetworkLinkUp(ifc); err != nil {
return fmt.Errorf("Could not bring up network link %s: %s", ifc.Name, err)
}
}
}
return nil
}