Merge pull request #206 from milosgajdos83/vlan-macvlan
Added support for VLAN and MAC VLAN interfaces plus did a bit of refactoring.
This commit is contained in:
commit
c744f6470e
|
@ -12,16 +12,25 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
IFNAMSIZ = 16
|
||||
DEFAULT_CHANGE = 0xFFFFFFFF
|
||||
IFLA_INFO_KIND = 1
|
||||
IFLA_INFO_DATA = 2
|
||||
VETH_INFO_PEER = 1
|
||||
IFLA_NET_NS_FD = 28
|
||||
IFLA_ADDRESS = 1
|
||||
SIOC_BRADDBR = 0x89a0
|
||||
SIOC_BRDELBR = 0x89a1
|
||||
SIOC_BRADDIF = 0x89a2
|
||||
IFNAMSIZ = 16
|
||||
DEFAULT_CHANGE = 0xFFFFFFFF
|
||||
IFLA_INFO_KIND = 1
|
||||
IFLA_INFO_DATA = 2
|
||||
VETH_INFO_PEER = 1
|
||||
IFLA_MACVLAN_MODE = 1
|
||||
IFLA_VLAN_ID = 1
|
||||
IFLA_NET_NS_FD = 28
|
||||
IFLA_ADDRESS = 1
|
||||
SIOC_BRADDBR = 0x89a0
|
||||
SIOC_BRDELBR = 0x89a1
|
||||
SIOC_BRADDIF = 0x89a2
|
||||
)
|
||||
|
||||
const (
|
||||
MACVLAN_MODE_PRIVATE = 1 << iota
|
||||
MACVLAN_MODE_VEPA
|
||||
MACVLAN_MODE_BRIDGE
|
||||
MACVLAN_MODE_PASSTHRU
|
||||
)
|
||||
|
||||
var nextSeqNr uint32
|
||||
|
@ -385,7 +394,7 @@ func nonZeroTerminated(s string) []byte {
|
|||
}
|
||||
|
||||
// Add a new network link of a specified type.
|
||||
// This is identical to running: ip add link $name type $linkType
|
||||
// This is identical to running: ip link add $name type $linkType
|
||||
func NetworkLinkAdd(name string, linkType string) error {
|
||||
if name == "" || linkType == "" {
|
||||
return fmt.Errorf("Neither link name nor link type can be empty!")
|
||||
|
@ -603,22 +612,18 @@ func NetworkSetNoMaster(iface *net.Interface) error {
|
|||
return networkMasterAction(iface, data)
|
||||
}
|
||||
|
||||
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
|
||||
func networkSetNsAction(iface *net.Interface, rtattr *RtAttr) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(iface.Index)
|
||||
msg.Change = DEFAULT_CHANGE
|
||||
wb.AddData(msg)
|
||||
wb.AddData(uint32Attr(syscall.IFLA_NET_NS_PID, uint32(nspid)))
|
||||
wb.AddData(rtattr)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
|
@ -627,7 +632,29 @@ func NetworkSetNsPid(iface *net.Interface, nspid int) error {
|
|||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
// Move a particular network interface to a particular network namespace
|
||||
// specified by PID. This is idential to running: ip link set dev $name netns $pid
|
||||
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
|
||||
data := uint32Attr(syscall.IFLA_NET_NS_PID, uint32(nspid))
|
||||
return networkSetNsAction(iface, data)
|
||||
}
|
||||
|
||||
// Move a particular network interface to a particular mounted
|
||||
// network namespace specified by file descriptor.
|
||||
// This is idential to running: ip link set dev $name netns $fd
|
||||
func NetworkSetNsFd(iface *net.Interface, fd int) error {
|
||||
data := uint32Attr(IFLA_NET_NS_FD, uint32(fd))
|
||||
return networkSetNsAction(iface, data)
|
||||
}
|
||||
|
||||
// Rname a particular interface to a different name
|
||||
// !!! Note that you can't rename an active interface. You need to bring it down before renaming it.
|
||||
// This is identical to running: ip link set dev ${oldName} name ${newName}
|
||||
func NetworkChangeName(iface *net.Interface, newName string) error {
|
||||
if len(newName) >= IFNAMSIZ {
|
||||
return fmt.Errorf("Interface name %s too long", newName)
|
||||
}
|
||||
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -637,12 +664,12 @@ func NetworkSetNsFd(iface *net.Interface, fd int) error {
|
|||
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
msg.Type = syscall.RTM_SETLINK
|
||||
msg.Flags = syscall.NLM_F_REQUEST
|
||||
msg.Index = int32(iface.Index)
|
||||
msg.Change = DEFAULT_CHANGE
|
||||
wb.AddData(msg)
|
||||
wb.AddData(uint32Attr(IFLA_NET_NS_FD, uint32(fd)))
|
||||
|
||||
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(newName))
|
||||
wb.AddData(nameData)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
|
@ -651,6 +678,122 @@ func NetworkSetNsFd(iface *net.Interface, fd int) error {
|
|||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
// Add a new VETH pair link on the host
|
||||
// This is identical to running: ip link add name $name type veth peer name $peername
|
||||
func NetworkCreateVethPair(name1, name2 string) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
wb.AddData(msg)
|
||||
|
||||
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1))
|
||||
wb.AddData(nameData)
|
||||
|
||||
nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||
newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth"))
|
||||
nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil)
|
||||
nest3 := newRtAttrChild(nest2, VETH_INFO_PEER, nil)
|
||||
|
||||
newIfInfomsgChild(nest3, syscall.AF_UNSPEC)
|
||||
newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2))
|
||||
|
||||
wb.AddData(nest1)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
// Add a new VLAN interface with masterDev as its upper device
|
||||
// This is identical to running:
|
||||
// ip link add name $name link $masterdev type vlan id $id
|
||||
func NetworkLinkAddVlan(masterDev, vlanDev string, vlanId uint16) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
masterDevIfc, err := net.InterfaceByName(masterDev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
wb.AddData(msg)
|
||||
|
||||
nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||
newRtAttrChild(nest1, IFLA_INFO_KIND, nonZeroTerminated("vlan"))
|
||||
|
||||
nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil)
|
||||
vlanData := make([]byte, 2)
|
||||
native.PutUint16(vlanData, vlanId)
|
||||
newRtAttrChild(nest2, IFLA_VLAN_ID, vlanData)
|
||||
wb.AddData(nest1)
|
||||
|
||||
wb.AddData(uint32Attr(syscall.IFLA_LINK, uint32(masterDevIfc.Index)))
|
||||
wb.AddData(newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(vlanDev)))
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
// Add MAC VLAN network interface with masterDev as its upper device
|
||||
// This is identical to running:
|
||||
// ip link add name $name link $masterdev type macvlan mode $mode
|
||||
func NetworkLinkAddMacVlan(masterDev, macVlanDev string, mode string) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
macVlan := map[string]uint32{
|
||||
"private": MACVLAN_MODE_PRIVATE,
|
||||
"vepa": MACVLAN_MODE_VEPA,
|
||||
"bridge": MACVLAN_MODE_BRIDGE,
|
||||
"passthru": MACVLAN_MODE_PASSTHRU,
|
||||
}
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
masterDevIfc, err := net.InterfaceByName(masterDev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
wb.AddData(msg)
|
||||
|
||||
nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||
newRtAttrChild(nest1, IFLA_INFO_KIND, nonZeroTerminated("macvlan"))
|
||||
|
||||
nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil)
|
||||
macVlanData := make([]byte, 4)
|
||||
native.PutUint32(macVlanData, macVlan[mode])
|
||||
newRtAttrChild(nest2, IFLA_MACVLAN_MODE, macVlanData)
|
||||
wb.AddData(nest1)
|
||||
|
||||
wb.AddData(uint32Attr(syscall.IFLA_LINK, uint32(masterDevIfc.Index)))
|
||||
wb.AddData(newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(macVlanDev)))
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
func networkLinkIpAction(action, flags int, ifa IfAddr) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
|
@ -906,60 +1049,6 @@ func AddDefaultGw(ip, device string) error {
|
|||
return AddRoute("", "", ip, device)
|
||||
}
|
||||
|
||||
func NetworkChangeName(iface *net.Interface, newName string) error {
|
||||
if len(newName) >= IFNAMSIZ {
|
||||
return fmt.Errorf("Interface name %s too long", newName)
|
||||
}
|
||||
|
||||
fd, err := getIfSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
data := [IFNAMSIZ * 2]byte{}
|
||||
// the "-1"s here are very important for ensuring we get proper null
|
||||
// termination of our new C strings
|
||||
copy(data[:IFNAMSIZ-1], iface.Name)
|
||||
copy(data[IFNAMSIZ:IFNAMSIZ*2-1], newName)
|
||||
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NetworkCreateVethPair(name1, name2 string) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
wb.AddData(msg)
|
||||
|
||||
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name1))
|
||||
wb.AddData(nameData)
|
||||
|
||||
nest1 := newRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||
newRtAttrChild(nest1, IFLA_INFO_KIND, zeroTerminated("veth"))
|
||||
nest2 := newRtAttrChild(nest1, IFLA_INFO_DATA, nil)
|
||||
nest3 := newRtAttrChild(nest2, VETH_INFO_PEER, nil)
|
||||
|
||||
newIfInfomsgChild(nest3, syscall.AF_UNSPEC)
|
||||
newRtAttrChild(nest3, syscall.IFLA_IFNAME, zeroTerminated(name2))
|
||||
|
||||
wb.AddData(nest1)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
// THIS CODE DOES NOT COMMUNICATE WITH KERNEL VIA RTNETLINK INTERFACE
|
||||
// IT IS HERE FOR BACKWARDS COMPATIBILITY WITH OLDER LINUX KERNELS
|
||||
// WHICH SHIP WITH OLDER NOT ENTIRELY FUNCTIONAL VERSION OF NETLINK
|
||||
|
@ -1095,3 +1184,26 @@ func SetMacAddress(name, addr string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ChangeName(iface *net.Interface, newName string) error {
|
||||
if len(newName) >= IFNAMSIZ {
|
||||
return fmt.Errorf("Interface name %s too long", newName)
|
||||
}
|
||||
|
||||
fd, err := getIfSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
data := [IFNAMSIZ * 2]byte{}
|
||||
// the "-1"s here are very important for ensuring we get proper null
|
||||
// termination of our new C strings
|
||||
copy(data[:IFNAMSIZ-1], iface.Name)
|
||||
copy(data[IFNAMSIZ:IFNAMSIZ*2-1], newName)
|
||||
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.SIOCSIFNAME, uintptr(unsafe.Pointer(&data[0]))); errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -180,6 +180,74 @@ func TestNetworkSetMasterNoMaster(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestNetworkChangeName(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
tl := testLink{"tstEth", "dummy"}
|
||||
newName := "newTst"
|
||||
|
||||
addLink(t, tl.name, tl.linkType)
|
||||
|
||||
linkIfc := readLink(t, tl.name)
|
||||
if err := NetworkChangeName(linkIfc, newName); err != nil {
|
||||
deleteLink(t, tl.name)
|
||||
t.Fatalf("Could not change %#v interface name to %s: %s", tl, newName, err)
|
||||
}
|
||||
|
||||
readLink(t, newName)
|
||||
deleteLink(t, newName)
|
||||
}
|
||||
|
||||
func TestNetworkLinkAddVlan(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
tl := struct {
|
||||
name string
|
||||
id uint16
|
||||
}{
|
||||
name: "tstVlan",
|
||||
id: 32,
|
||||
}
|
||||
masterLink := testLink{"tstEth", "dummy"}
|
||||
|
||||
addLink(t, masterLink.name, masterLink.linkType)
|
||||
defer deleteLink(t, masterLink.name)
|
||||
|
||||
if err := NetworkLinkAddVlan(masterLink.name, tl.name, tl.id); err != nil {
|
||||
t.Fatalf("Unable to create %#v VLAN interface: %s", tl, err)
|
||||
}
|
||||
|
||||
readLink(t, tl.name)
|
||||
}
|
||||
|
||||
func TestNetworkLinkAddMacVlan(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
}
|
||||
|
||||
tl := struct {
|
||||
name string
|
||||
mode string
|
||||
}{
|
||||
name: "tstVlan",
|
||||
mode: "private",
|
||||
}
|
||||
masterLink := testLink{"tstEth", "dummy"}
|
||||
|
||||
addLink(t, masterLink.name, masterLink.linkType)
|
||||
defer deleteLink(t, masterLink.name)
|
||||
|
||||
if err := NetworkLinkAddMacVlan(masterLink.name, tl.name, tl.mode); err != nil {
|
||||
t.Fatalf("Unable to create %#v MAC VLAN interface: %s", tl, err)
|
||||
}
|
||||
|
||||
readLink(t, tl.name)
|
||||
}
|
||||
|
||||
func TestAddDelNetworkIp(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return
|
||||
|
@ -232,7 +300,7 @@ func TestCreateVethPair(t *testing.T) {
|
|||
}
|
||||
|
||||
//
|
||||
// netlink package test which do not use RTNETLINK
|
||||
// netlink package tests which do not use RTNETLINK
|
||||
//
|
||||
func TestCreateBridgeWithMac(t *testing.T) {
|
||||
if testing.Short() {
|
||||
|
|
Loading…
Reference in New Issue