Merge pull request #197 from milosgajdos83/netlink-tests
Refactored and added more tests.Cleaned up netlink a bit.
This commit is contained in:
commit
930cdd82c9
|
@ -18,6 +18,7 @@ const (
|
||||||
IFLA_INFO_DATA = 2
|
IFLA_INFO_DATA = 2
|
||||||
VETH_INFO_PEER = 1
|
VETH_INFO_PEER = 1
|
||||||
IFLA_NET_NS_FD = 28
|
IFLA_NET_NS_FD = 28
|
||||||
|
IFLA_ADDRESS = 1
|
||||||
SIOC_BRADDBR = 0x89a0
|
SIOC_BRADDBR = 0x89a0
|
||||||
SIOC_BRDELBR = 0x89a1
|
SIOC_BRDELBR = 0x89a1
|
||||||
SIOC_BRADDIF = 0x89a2
|
SIOC_BRADDIF = 0x89a2
|
||||||
|
@ -375,10 +376,19 @@ outer:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new route table entry.
|
func zeroTerminated(s string) []byte {
|
||||||
func AddRoute(destination, source, gateway, device string) error {
|
return []byte(s + "\000")
|
||||||
if destination == "" && source == "" && gateway == "" {
|
}
|
||||||
return fmt.Errorf("one of destination, source or gateway must not be blank")
|
|
||||||
|
func nonZeroTerminated(s string) []byte {
|
||||||
|
return []byte(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new network link of a specified type.
|
||||||
|
// This is identical to running: ip add link $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!")
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := getNetlinkSocket()
|
s, err := getNetlinkSocket()
|
||||||
|
@ -387,101 +397,58 @@ func AddRoute(destination, source, gateway, device string) error {
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||||
msg := newRtMsg()
|
|
||||||
currentFamily := -1
|
|
||||||
var rtAttrs []*RtAttr
|
|
||||||
|
|
||||||
if destination != "" {
|
|
||||||
destIP, destNet, err := net.ParseCIDR(destination)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("destination CIDR %s couldn't be parsed", destination)
|
|
||||||
}
|
|
||||||
destFamily := getIpFamily(destIP)
|
|
||||||
currentFamily = destFamily
|
|
||||||
destLen, bits := destNet.Mask.Size()
|
|
||||||
if destLen == 0 && bits == 0 {
|
|
||||||
return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination)
|
|
||||||
}
|
|
||||||
msg.Family = uint8(destFamily)
|
|
||||||
msg.Dst_len = uint8(destLen)
|
|
||||||
var destData []byte
|
|
||||||
if destFamily == syscall.AF_INET {
|
|
||||||
destData = destIP.To4()
|
|
||||||
} else {
|
|
||||||
destData = destIP.To16()
|
|
||||||
}
|
|
||||||
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData))
|
|
||||||
}
|
|
||||||
|
|
||||||
if source != "" {
|
|
||||||
srcIP, srcNet, err := net.ParseCIDR(source)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("source CIDR %s couldn't be parsed", source)
|
|
||||||
}
|
|
||||||
srcFamily := getIpFamily(srcIP)
|
|
||||||
if currentFamily != -1 && currentFamily != srcFamily {
|
|
||||||
return fmt.Errorf("source and destination ip were not the same IP family")
|
|
||||||
}
|
|
||||||
currentFamily = srcFamily
|
|
||||||
srcLen, bits := srcNet.Mask.Size()
|
|
||||||
if srcLen == 0 && bits == 0 {
|
|
||||||
return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source)
|
|
||||||
}
|
|
||||||
msg.Family = uint8(srcFamily)
|
|
||||||
msg.Src_len = uint8(srcLen)
|
|
||||||
var srcData []byte
|
|
||||||
if srcFamily == syscall.AF_INET {
|
|
||||||
srcData = srcIP.To4()
|
|
||||||
} else {
|
|
||||||
srcData = srcIP.To16()
|
|
||||||
}
|
|
||||||
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData))
|
|
||||||
}
|
|
||||||
|
|
||||||
if gateway != "" {
|
|
||||||
gwIP := net.ParseIP(gateway)
|
|
||||||
if gwIP == nil {
|
|
||||||
return fmt.Errorf("gateway IP %s couldn't be parsed", gateway)
|
|
||||||
}
|
|
||||||
gwFamily := getIpFamily(gwIP)
|
|
||||||
if currentFamily != -1 && currentFamily != gwFamily {
|
|
||||||
return fmt.Errorf("gateway, source, and destination ip were not the same IP family")
|
|
||||||
}
|
|
||||||
msg.Family = uint8(gwFamily)
|
|
||||||
var gwData []byte
|
|
||||||
if gwFamily == syscall.AF_INET {
|
|
||||||
gwData = gwIP.To4()
|
|
||||||
} else {
|
|
||||||
gwData = gwIP.To16()
|
|
||||||
}
|
|
||||||
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
wb.AddData(msg)
|
wb.AddData(msg)
|
||||||
for _, attr := range rtAttrs {
|
|
||||||
wb.AddData(attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
iface, err := net.InterfaceByName(device)
|
linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil)
|
||||||
if err != nil {
|
newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType))
|
||||||
return err
|
wb.AddData(linkInfo)
|
||||||
}
|
|
||||||
wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index)))
|
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
|
||||||
|
wb.AddData(nameData)
|
||||||
|
|
||||||
if err := s.Send(wb); err != nil {
|
if err := s.Send(wb); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.HandleAck(wb.Seq)
|
return s.HandleAck(wb.Seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new default gateway. Identical to:
|
// Delete a network link.
|
||||||
// ip route add default via $ip
|
// This is identical to running: ip link del $name
|
||||||
func AddDefaultGw(ip, device string) error {
|
func NetworkLinkDel(name string) error {
|
||||||
return AddRoute("", "", ip, device)
|
if name == "" {
|
||||||
|
return fmt.Errorf("Network link name can not be empty!")
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
msg.Index = int32(iface.Index)
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bring up a particular network interface
|
// Bring up a particular network interface.
|
||||||
|
// This is identical to running: ip link set dev $name up
|
||||||
func NetworkLinkUp(iface *net.Interface) error {
|
func NetworkLinkUp(iface *net.Interface) error {
|
||||||
s, err := getNetlinkSocket()
|
s, err := getNetlinkSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -492,9 +459,9 @@ func NetworkLinkUp(iface *net.Interface) error {
|
||||||
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
msg.Change = syscall.IFF_UP
|
|
||||||
msg.Flags = syscall.IFF_UP
|
|
||||||
msg.Index = int32(iface.Index)
|
msg.Index = int32(iface.Index)
|
||||||
|
msg.Flags = syscall.IFF_UP
|
||||||
|
msg.Change = syscall.IFF_UP
|
||||||
wb.AddData(msg)
|
wb.AddData(msg)
|
||||||
|
|
||||||
if err := s.Send(wb); err != nil {
|
if err := s.Send(wb); err != nil {
|
||||||
|
@ -504,6 +471,8 @@ func NetworkLinkUp(iface *net.Interface) error {
|
||||||
return s.HandleAck(wb.Seq)
|
return s.HandleAck(wb.Seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bring down a particular network interface.
|
||||||
|
// This is identical to running: ip link set $name down
|
||||||
func NetworkLinkDown(iface *net.Interface) error {
|
func NetworkLinkDown(iface *net.Interface) error {
|
||||||
s, err := getNetlinkSocket()
|
s, err := getNetlinkSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -514,9 +483,9 @@ func NetworkLinkDown(iface *net.Interface) error {
|
||||||
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
msg.Change = syscall.IFF_UP
|
|
||||||
msg.Flags = 0 & ^syscall.IFF_UP
|
|
||||||
msg.Index = int32(iface.Index)
|
msg.Index = int32(iface.Index)
|
||||||
|
msg.Flags = 0 & ^syscall.IFF_UP
|
||||||
|
msg.Change = DEFAULT_CHANGE
|
||||||
wb.AddData(msg)
|
wb.AddData(msg)
|
||||||
|
|
||||||
if err := s.Send(wb); err != nil {
|
if err := s.Send(wb); err != nil {
|
||||||
|
@ -526,6 +495,53 @@ func NetworkLinkDown(iface *net.Interface) error {
|
||||||
return s.HandleAck(wb.Seq)
|
return s.HandleAck(wb.Seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set link layer address ie. MAC Address.
|
||||||
|
// This is identical to running: ip link set dev $name address $macaddress
|
||||||
|
func NetworkSetMacAddress(iface *net.Interface, macaddr string) error {
|
||||||
|
s, err := getNetlinkSocket()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
hwaddr, err := net.ParseMAC(macaddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
MULTICAST byte = 0x1
|
||||||
|
LOCALOUI byte = 0x2
|
||||||
|
)
|
||||||
|
|
||||||
|
if hwaddr[0]&0x1 == MULTICAST || hwaddr[0]&0x2 != LOCALOUI {
|
||||||
|
return fmt.Errorf("Incorrect Local MAC Address specified: %s", macaddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_SETLINK, syscall.NLM_F_ACK)
|
||||||
|
|
||||||
|
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||||
|
msg.Index = int32(iface.Index)
|
||||||
|
msg.Change = DEFAULT_CHANGE
|
||||||
|
wb.AddData(msg)
|
||||||
|
|
||||||
|
macdata := make([]byte, 6)
|
||||||
|
copy(macdata, hwaddr)
|
||||||
|
data := newRtAttr(IFLA_ADDRESS, macdata)
|
||||||
|
wb.AddData(data)
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set link Maximum Transmission Unit
|
||||||
|
// This is identical to running: ip link set dev $name mtu $MTU
|
||||||
|
// bridge is a bitch here https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=292088
|
||||||
|
// https://bugzilla.redhat.com/show_bug.cgi?id=697021
|
||||||
|
// There is a discussion about how to deal with ifcs joining bridge with MTU > 1500
|
||||||
|
// Regular network nterfaces do seem to work though!
|
||||||
func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
||||||
s, err := getNetlinkSocket()
|
s, err := getNetlinkSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -549,8 +565,7 @@ func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
||||||
return s.HandleAck(wb.Seq)
|
return s.HandleAck(wb.Seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
// same as ip link set $name master $master
|
func networkMasterAction(iface *net.Interface, rtattr *RtAttr) error {
|
||||||
func NetworkSetMaster(iface, master *net.Interface) error {
|
|
||||||
s, err := getNetlinkSocket()
|
s, err := getNetlinkSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -565,7 +580,7 @@ func NetworkSetMaster(iface, master *net.Interface) error {
|
||||||
msg.Index = int32(iface.Index)
|
msg.Index = int32(iface.Index)
|
||||||
msg.Change = DEFAULT_CHANGE
|
msg.Change = DEFAULT_CHANGE
|
||||||
wb.AddData(msg)
|
wb.AddData(msg)
|
||||||
wb.AddData(uint32Attr(syscall.IFLA_MASTER, uint32(master.Index)))
|
wb.AddData(rtattr)
|
||||||
|
|
||||||
if err := s.Send(wb); err != nil {
|
if err := s.Send(wb); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -574,6 +589,20 @@ func NetworkSetMaster(iface, master *net.Interface) error {
|
||||||
return s.HandleAck(wb.Seq)
|
return s.HandleAck(wb.Seq)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add an interface to bridge.
|
||||||
|
// This is identical to running: ip link set $name master $master
|
||||||
|
func NetworkSetMaster(iface, master *net.Interface) error {
|
||||||
|
data := uint32Attr(syscall.IFLA_MASTER, uint32(master.Index))
|
||||||
|
return networkMasterAction(iface, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove an interface from the bridge
|
||||||
|
// This is is identical to to running: ip link $name set nomaster
|
||||||
|
func NetworkSetNoMaster(iface *net.Interface) error {
|
||||||
|
data := uint32Attr(syscall.IFLA_MASTER, 0)
|
||||||
|
return networkMasterAction(iface, data)
|
||||||
|
}
|
||||||
|
|
||||||
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
|
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
|
||||||
s, err := getNetlinkSocket()
|
s, err := getNetlinkSocket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -679,77 +708,6 @@ func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func zeroTerminated(s string) []byte {
|
|
||||||
return []byte(s + "\000")
|
|
||||||
}
|
|
||||||
|
|
||||||
func nonZeroTerminated(s string) []byte {
|
|
||||||
return []byte(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a new network link of a specified type. This is identical to
|
|
||||||
// running: ip add link $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!")
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
linkInfo := newRtAttr(syscall.IFLA_LINKINFO, nil)
|
|
||||||
newRtAttrChild(linkInfo, IFLA_INFO_KIND, nonZeroTerminated(linkType))
|
|
||||||
wb.AddData(linkInfo)
|
|
||||||
|
|
||||||
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
|
|
||||||
wb.AddData(nameData)
|
|
||||||
|
|
||||||
if err := s.Send(wb); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.HandleAck(wb.Seq)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete a network link. This is identical to
|
|
||||||
// running: ip link del $name
|
|
||||||
func NetworkLinkDel(name string) error {
|
|
||||||
if name == "" {
|
|
||||||
return fmt.Errorf("Network link name can not be empty!")
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := getNetlinkSocket()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer s.Close()
|
|
||||||
|
|
||||||
iface, err := net.InterfaceByName(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
wb := newNetlinkRequest(syscall.RTM_DELLINK, syscall.NLM_F_ACK)
|
|
||||||
|
|
||||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
|
||||||
msg.Index = int32(iface.Index)
|
|
||||||
wb.AddData(msg)
|
|
||||||
|
|
||||||
if err := s.Send(wb); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.HandleAck(wb.Seq)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an array of IPNet for all the currently routed subnets on ipv4
|
// Returns an array of IPNet for all the currently routed subnets on ipv4
|
||||||
// This is similar to the first column of "ip route" output
|
// This is similar to the first column of "ip route" output
|
||||||
func NetworkGetRoutes() ([]Route, error) {
|
func NetworkGetRoutes() ([]Route, error) {
|
||||||
|
@ -842,20 +800,110 @@ outer:
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIfSocket() (fd int, err error) {
|
// Add a new route table entry.
|
||||||
for _, socket := range []int{
|
func AddRoute(destination, source, gateway, device string) error {
|
||||||
syscall.AF_INET,
|
if destination == "" && source == "" && gateway == "" {
|
||||||
syscall.AF_PACKET,
|
return fmt.Errorf("one of destination, source or gateway must not be blank")
|
||||||
syscall.AF_INET6,
|
}
|
||||||
} {
|
|
||||||
if fd, err = syscall.Socket(socket, syscall.SOCK_DGRAM, 0); err == nil {
|
s, err := getNetlinkSocket()
|
||||||
break
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||||
|
msg := newRtMsg()
|
||||||
|
currentFamily := -1
|
||||||
|
var rtAttrs []*RtAttr
|
||||||
|
|
||||||
|
if destination != "" {
|
||||||
|
destIP, destNet, err := net.ParseCIDR(destination)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("destination CIDR %s couldn't be parsed", destination)
|
||||||
}
|
}
|
||||||
|
destFamily := getIpFamily(destIP)
|
||||||
|
currentFamily = destFamily
|
||||||
|
destLen, bits := destNet.Mask.Size()
|
||||||
|
if destLen == 0 && bits == 0 {
|
||||||
|
return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination)
|
||||||
|
}
|
||||||
|
msg.Family = uint8(destFamily)
|
||||||
|
msg.Dst_len = uint8(destLen)
|
||||||
|
var destData []byte
|
||||||
|
if destFamily == syscall.AF_INET {
|
||||||
|
destData = destIP.To4()
|
||||||
|
} else {
|
||||||
|
destData = destIP.To16()
|
||||||
|
}
|
||||||
|
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData))
|
||||||
}
|
}
|
||||||
if err == nil {
|
|
||||||
return fd, nil
|
if source != "" {
|
||||||
|
srcIP, srcNet, err := net.ParseCIDR(source)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("source CIDR %s couldn't be parsed", source)
|
||||||
|
}
|
||||||
|
srcFamily := getIpFamily(srcIP)
|
||||||
|
if currentFamily != -1 && currentFamily != srcFamily {
|
||||||
|
return fmt.Errorf("source and destination ip were not the same IP family")
|
||||||
|
}
|
||||||
|
currentFamily = srcFamily
|
||||||
|
srcLen, bits := srcNet.Mask.Size()
|
||||||
|
if srcLen == 0 && bits == 0 {
|
||||||
|
return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source)
|
||||||
|
}
|
||||||
|
msg.Family = uint8(srcFamily)
|
||||||
|
msg.Src_len = uint8(srcLen)
|
||||||
|
var srcData []byte
|
||||||
|
if srcFamily == syscall.AF_INET {
|
||||||
|
srcData = srcIP.To4()
|
||||||
|
} else {
|
||||||
|
srcData = srcIP.To16()
|
||||||
|
}
|
||||||
|
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData))
|
||||||
}
|
}
|
||||||
return -1, err
|
|
||||||
|
if gateway != "" {
|
||||||
|
gwIP := net.ParseIP(gateway)
|
||||||
|
if gwIP == nil {
|
||||||
|
return fmt.Errorf("gateway IP %s couldn't be parsed", gateway)
|
||||||
|
}
|
||||||
|
gwFamily := getIpFamily(gwIP)
|
||||||
|
if currentFamily != -1 && currentFamily != gwFamily {
|
||||||
|
return fmt.Errorf("gateway, source, and destination ip were not the same IP family")
|
||||||
|
}
|
||||||
|
msg.Family = uint8(gwFamily)
|
||||||
|
var gwData []byte
|
||||||
|
if gwFamily == syscall.AF_INET {
|
||||||
|
gwData = gwIP.To4()
|
||||||
|
} else {
|
||||||
|
gwData = gwIP.To16()
|
||||||
|
}
|
||||||
|
rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData))
|
||||||
|
}
|
||||||
|
|
||||||
|
wb.AddData(msg)
|
||||||
|
for _, attr := range rtAttrs {
|
||||||
|
wb.AddData(attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(device)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wb.AddData(uint32Attr(syscall.RTA_OIF, uint32(iface.Index)))
|
||||||
|
|
||||||
|
if err := s.Send(wb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.HandleAck(wb.Seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new default gateway. Identical to:
|
||||||
|
// ip route add default via $ip
|
||||||
|
func AddDefaultGw(ip, device string) error {
|
||||||
|
return AddRoute("", "", ip, device)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NetworkChangeName(iface *net.Interface, newName string) error {
|
func NetworkChangeName(iface *net.Interface, newName string) error {
|
||||||
|
@ -912,6 +960,25 @@ func NetworkCreateVethPair(name1, name2 string) error {
|
||||||
return s.HandleAck(wb.Seq)
|
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
|
||||||
|
func getIfSocket() (fd int, err error) {
|
||||||
|
for _, socket := range []int{
|
||||||
|
syscall.AF_INET,
|
||||||
|
syscall.AF_PACKET,
|
||||||
|
syscall.AF_INET6,
|
||||||
|
} {
|
||||||
|
if fd, err = syscall.Socket(socket, syscall.SOCK_DGRAM, 0); err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return fd, nil
|
||||||
|
}
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
// Create the actual bridge device. This is more backward-compatible than
|
// Create the actual bridge device. This is more backward-compatible than
|
||||||
// netlink.NetworkLinkAdd and works on RHEL 6.
|
// netlink.NetworkLinkAdd and works on RHEL 6.
|
||||||
func CreateBridge(name string, setMacAddr bool) error {
|
func CreateBridge(name string, setMacAddr bool) error {
|
||||||
|
@ -933,7 +1000,7 @@ func CreateBridge(name string, setMacAddr bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if setMacAddr {
|
if setMacAddr {
|
||||||
return NetworkSetMacAddress(name, randMacAddr())
|
return SetMacAddress(name, randMacAddr())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -999,7 +1066,7 @@ func randMacAddr() string {
|
||||||
return hw.String()
|
return hw.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NetworkSetMacAddress(name, addr string) error {
|
func SetMacAddress(name, addr string) error {
|
||||||
if len(name) >= IFNAMSIZ {
|
if len(name) >= IFNAMSIZ {
|
||||||
return fmt.Errorf("Interface name %s too long", name)
|
return fmt.Errorf("Interface name %s too long", name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,50 @@ package netlink
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type testLink struct {
|
||||||
|
name string
|
||||||
|
linkType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLink(t *testing.T, name string, linkType string) {
|
||||||
|
if err := NetworkLinkAdd(name, linkType); err != nil {
|
||||||
|
t.Fatalf("Unable to create %s link: %s", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readLink(t *testing.T, name string) *net.Interface {
|
||||||
|
iface, err := net.InterfaceByName(name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not find %s interface: %s", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return iface
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteLink(t *testing.T, name string) {
|
||||||
|
if err := NetworkLinkDel(name); err != nil {
|
||||||
|
t.Fatalf("Unable to delete %s link: %s", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func upLink(t *testing.T, name string) {
|
||||||
|
iface := readLink(t, name)
|
||||||
|
if err := NetworkLinkUp(iface); err != nil {
|
||||||
|
t.Fatalf("Could not bring UP %#v interface: %s", iface, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func downLink(t *testing.T, name string) {
|
||||||
|
iface := readLink(t, name)
|
||||||
|
if err := NetworkLinkDown(iface); err != nil {
|
||||||
|
t.Fatalf("Could not bring DOWN %#v interface: %s", iface, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ipAssigned(iface *net.Interface, ip net.IP) bool {
|
func ipAssigned(iface *net.Interface, ip net.IP) bool {
|
||||||
addrs, _ := iface.Addrs()
|
addrs, _ := iface.Addrs()
|
||||||
|
|
||||||
|
@ -19,6 +60,126 @@ func ipAssigned(iface *net.Interface, ip net.IP) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNetworkLinkAddDel(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testLinks := []testLink{
|
||||||
|
{"tstEth", "dummy"},
|
||||||
|
{"tstBr", "bridge"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tl := range testLinks {
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
readLink(t, tl.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkLinkUpDown(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := testLink{name: "tstEth", linkType: "dummy"}
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
|
||||||
|
upLink(t, tl.name)
|
||||||
|
ifcAfterUp := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if (ifcAfterUp.Flags & syscall.IFF_UP) != syscall.IFF_UP {
|
||||||
|
t.Fatalf("Could not bring UP %#v initerface", tl)
|
||||||
|
}
|
||||||
|
|
||||||
|
downLink(t, tl.name)
|
||||||
|
ifcAfterDown := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if (ifcAfterDown.Flags & syscall.IFF_UP) == syscall.IFF_UP {
|
||||||
|
t.Fatalf("Could not bring DOWN %#v initerface", tl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkSetMacAddress(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := testLink{name: "tstEth", linkType: "dummy"}
|
||||||
|
macaddr := "22:ce:e0:99:63:6f"
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
|
||||||
|
ifcBeforeSet := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if err := NetworkSetMacAddress(ifcBeforeSet, macaddr); err != nil {
|
||||||
|
t.Fatalf("Could not set %s MAC address on %#v interface: err", macaddr, tl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ifcAfterSet := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if ifcAfterSet.HardwareAddr.String() != macaddr {
|
||||||
|
t.Fatalf("Could not set %s MAC address on %#v interface", macaddr, tl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkSetMTU(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := testLink{name: "tstEth", linkType: "dummy"}
|
||||||
|
mtu := 1400
|
||||||
|
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
|
||||||
|
ifcBeforeSet := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if err := NetworkSetMTU(ifcBeforeSet, mtu); err != nil {
|
||||||
|
t.Fatalf("Could not set %d MTU on %#v interface: err", mtu, tl, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ifcAfterSet := readLink(t, tl.name)
|
||||||
|
|
||||||
|
if ifcAfterSet.MTU != mtu {
|
||||||
|
t.Fatalf("Could not set %d MTU on %#v interface", mtu, tl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNetworkSetMasterNoMaster(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
master := testLink{"tstBr", "bridge"}
|
||||||
|
slave := testLink{"tstEth", "dummy"}
|
||||||
|
testLinks := []testLink{master, slave}
|
||||||
|
|
||||||
|
for _, tl := range testLinks {
|
||||||
|
addLink(t, tl.name, tl.linkType)
|
||||||
|
defer deleteLink(t, tl.name)
|
||||||
|
upLink(t, tl.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
masterIfc := readLink(t, master.name)
|
||||||
|
slaveIfc := readLink(t, slave.name)
|
||||||
|
if err := NetworkSetMaster(slaveIfc, masterIfc); err != nil {
|
||||||
|
t.Fatalf("Could not set %#v to be the master of %#v: %s", master, slave, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trying to figure out a way to test which will not break on RHEL6.
|
||||||
|
// We could check for existence of /sys/class/net/tstEth/upper_tstBr
|
||||||
|
// which should point to the ../tstBr which is the UPPER device i.e. network bridge
|
||||||
|
|
||||||
|
if err := NetworkSetNoMaster(slaveIfc); err != nil {
|
||||||
|
t.Fatalf("Could not UNset %#v master of %#v: %s", master, slave, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAddDelNetworkIp(t *testing.T) {
|
func TestAddDelNetworkIp(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
return
|
return
|
||||||
|
@ -35,7 +196,7 @@ func TestAddDelNetworkIp(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ipAssigned(iface, ip) {
|
if !ipAssigned(iface, ip) {
|
||||||
|
@ -43,7 +204,7 @@ func TestAddDelNetworkIp(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil {
|
if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatalf("Could not delete IP address %s from interface %#v: %s", ip.String(), iface, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ipAssigned(iface, ip) {
|
if ipAssigned(iface, ip) {
|
||||||
|
@ -51,6 +212,28 @@ func TestAddDelNetworkIp(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateVethPair(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
name1 = "veth1"
|
||||||
|
name2 = "veth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := NetworkCreateVethPair(name1, name2); err != nil {
|
||||||
|
t.Fatalf("Could not create veth pair %s %s: %s", name1, name2, err)
|
||||||
|
}
|
||||||
|
defer NetworkLinkDel(name1)
|
||||||
|
|
||||||
|
readLink(t, name1)
|
||||||
|
readLink(t, name2)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// netlink package test which do not use RTNETLINK
|
||||||
|
//
|
||||||
func TestCreateBridgeWithMac(t *testing.T) {
|
func TestCreateBridgeWithMac(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
return
|
return
|
||||||
|
@ -77,55 +260,6 @@ func TestCreateBridgeWithMac(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateBridgeLink(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
name := "mybrlink"
|
|
||||||
|
|
||||||
if err := NetworkLinkAdd(name, "bridge"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := net.InterfaceByName(name); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := NetworkLinkDel(name); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := net.InterfaceByName(name); err == nil {
|
|
||||||
t.Fatalf("expected error getting interface because %s bridge was deleted", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVethPair(t *testing.T) {
|
|
||||||
if testing.Short() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
name1 = "veth1"
|
|
||||||
name2 = "veth2"
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := NetworkCreateVethPair(name1, name2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer NetworkLinkDel(name1)
|
|
||||||
|
|
||||||
if _, err := net.InterfaceByName(name1); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := net.InterfaceByName(name2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetMACAddress(t *testing.T) {
|
func TestSetMACAddress(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
return
|
return
|
||||||
|
@ -139,7 +273,7 @@ func TestSetMACAddress(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer NetworkLinkDel(name)
|
defer NetworkLinkDel(name)
|
||||||
|
|
||||||
if err := NetworkSetMacAddress(name, mac); err != nil {
|
if err := SetMacAddress(name, mac); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue