Merge pull request #4011 from crosbymichael/add-netlink-functions

Add more netlink functions
This commit is contained in:
Michael Crosby 2014-02-18 19:37:52 -05:00
commit 113629efb1
3 changed files with 347 additions and 79 deletions

View File

@ -5,7 +5,15 @@
// netlink_darwin.go // netlink_darwin.go
package netlink package netlink
import "net" import (
"errors"
"net"
)
var (
ErrWrongSockType = errors.New("Wrong socket type")
ErrShortResponse = errors.New("Got short response from netlink")
)
// A Route is a subnet associated with the interface to reach it. // A Route is a subnet associated with the interface to reach it.
type Route struct { type Route struct {

View File

@ -10,6 +10,15 @@ import (
"unsafe" "unsafe"
) )
const (
IFNAMSIZ = 16
DEFAULT_CHANGE = 0xFFFFFFFF
IFLA_INFO_KIND = 1
IFLA_INFO_DATA = 2
VETH_INFO_PEER = 1
IFLA_NET_NS_FD = 28
)
var nextSeqNr int var nextSeqNr int
func nativeEndian() binary.ByteOrder { func nativeEndian() binary.ByteOrder {
@ -36,6 +45,7 @@ func getIpFamily(ip net.IP) int {
} }
type NetlinkRequestData interface { type NetlinkRequestData interface {
Len() int
ToWireFormat() []byte ToWireFormat() []byte
} }
@ -44,21 +54,24 @@ type IfInfomsg struct {
} }
func newIfInfomsg(family int) *IfInfomsg { func newIfInfomsg(family int) *IfInfomsg {
msg := &IfInfomsg{} return &IfInfomsg{
msg.Family = uint8(family) IfInfomsg: syscall.IfInfomsg{
msg.Type = uint16(0) Family: uint8(family),
msg.Index = int32(0) },
msg.Flags = uint32(0) }
msg.Change = uint32(0) }
func newIfInfomsgChild(parent *RtAttr, family int) *IfInfomsg {
msg := newIfInfomsg(family)
parent.children = append(parent.children, msg)
return msg return msg
} }
func (msg *IfInfomsg) ToWireFormat() []byte { func (msg *IfInfomsg) ToWireFormat() []byte {
native := nativeEndian() native := nativeEndian()
len := syscall.SizeofIfInfomsg length := syscall.SizeofIfInfomsg
b := make([]byte, len) b := make([]byte, length)
b[0] = msg.Family b[0] = msg.Family
b[1] = 0 b[1] = 0
native.PutUint16(b[2:4], msg.Type) native.PutUint16(b[2:4], msg.Type)
@ -68,26 +81,27 @@ func (msg *IfInfomsg) ToWireFormat() []byte {
return b return b
} }
func (msg *IfInfomsg) Len() int {
return syscall.SizeofIfInfomsg
}
type IfAddrmsg struct { type IfAddrmsg struct {
syscall.IfAddrmsg syscall.IfAddrmsg
} }
func newIfAddrmsg(family int) *IfAddrmsg { func newIfAddrmsg(family int) *IfAddrmsg {
msg := &IfAddrmsg{} return &IfAddrmsg{
msg.Family = uint8(family) IfAddrmsg: syscall.IfAddrmsg{
msg.Prefixlen = uint8(0) Family: uint8(family),
msg.Flags = uint8(0) },
msg.Scope = uint8(0) }
msg.Index = uint32(0)
return msg
} }
func (msg *IfAddrmsg) ToWireFormat() []byte { func (msg *IfAddrmsg) ToWireFormat() []byte {
native := nativeEndian() native := nativeEndian()
len := syscall.SizeofIfAddrmsg length := syscall.SizeofIfAddrmsg
b := make([]byte, len) b := make([]byte, length)
b[0] = msg.Family b[0] = msg.Family
b[1] = msg.Prefixlen b[1] = msg.Prefixlen
b[2] = msg.Flags b[2] = msg.Flags
@ -96,26 +110,31 @@ func (msg *IfAddrmsg) ToWireFormat() []byte {
return b return b
} }
func (msg *IfAddrmsg) Len() int {
return syscall.SizeofIfAddrmsg
}
type RtMsg struct { type RtMsg struct {
syscall.RtMsg syscall.RtMsg
} }
func newRtMsg(family int) *RtMsg { func newRtMsg(family int) *RtMsg {
msg := &RtMsg{} return &RtMsg{
msg.Family = uint8(family) RtMsg: syscall.RtMsg{
msg.Table = syscall.RT_TABLE_MAIN Family: uint8(family),
msg.Scope = syscall.RT_SCOPE_UNIVERSE Table: syscall.RT_TABLE_MAIN,
msg.Protocol = syscall.RTPROT_BOOT Scope: syscall.RT_SCOPE_UNIVERSE,
msg.Type = syscall.RTN_UNICAST Protocol: syscall.RTPROT_BOOT,
Type: syscall.RTN_UNICAST,
return msg },
}
} }
func (msg *RtMsg) ToWireFormat() []byte { func (msg *RtMsg) ToWireFormat() []byte {
native := nativeEndian() native := nativeEndian()
len := syscall.SizeofRtMsg length := syscall.SizeofRtMsg
b := make([]byte, len) b := make([]byte, length)
b[0] = msg.Family b[0] = msg.Family
b[1] = msg.Dst_len b[1] = msg.Dst_len
b[2] = msg.Src_len b[2] = msg.Src_len
@ -128,6 +147,10 @@ func (msg *RtMsg) ToWireFormat() []byte {
return b return b
} }
func (msg *RtMsg) Len() int {
return syscall.SizeofRtMsg
}
func rtaAlignOf(attrlen int) int { func rtaAlignOf(attrlen int) int {
return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1) return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
} }
@ -135,28 +158,59 @@ func rtaAlignOf(attrlen int) int {
type RtAttr struct { type RtAttr struct {
syscall.RtAttr syscall.RtAttr
Data []byte Data []byte
children []NetlinkRequestData
} }
func newRtAttr(attrType int, data []byte) *RtAttr { func newRtAttr(attrType int, data []byte) *RtAttr {
attr := &RtAttr{} return &RtAttr{
attr.Type = uint16(attrType) RtAttr: syscall.RtAttr{
attr.Data = data Type: uint16(attrType),
},
children: []NetlinkRequestData{},
Data: data,
}
}
func newRtAttrChild(parent *RtAttr, attrType int, data []byte) *RtAttr {
attr := newRtAttr(attrType, data)
parent.children = append(parent.children, attr)
return attr return attr
} }
func (attr *RtAttr) ToWireFormat() []byte { func (a *RtAttr) Len() int {
native := nativeEndian() l := 0
for _, child := range a.children {
len := syscall.SizeofRtAttr + len(attr.Data) l += child.Len() + syscall.SizeofRtAttr
b := make([]byte, rtaAlignOf(len)) }
native.PutUint16(b[0:2], uint16(len)) if l == 0 {
native.PutUint16(b[2:4], attr.Type) l++
for i, d := range attr.Data { }
b[4+i] = d return rtaAlignOf(l + len(a.Data))
} }
return b func (a *RtAttr) ToWireFormat() []byte {
native := nativeEndian()
length := a.Len()
buf := make([]byte, rtaAlignOf(length+syscall.SizeofRtAttr))
if a.Data != nil {
copy(buf[4:], a.Data)
} else {
next := 4
for _, child := range a.children {
childBuf := child.ToWireFormat()
copy(buf[next:], childBuf)
next += rtaAlignOf(len(childBuf))
}
}
if l := uint16(rtaAlignOf(length)); l != 0 {
native.PutUint16(buf[0:2], l+1)
}
native.PutUint16(buf[2:4], a.Type)
return buf
} }
type NetlinkRequest struct { type NetlinkRequest struct {
@ -171,7 +225,7 @@ func (rr *NetlinkRequest) ToWireFormat() []byte {
dataBytes := make([][]byte, len(rr.Data)) dataBytes := make([][]byte, len(rr.Data))
for i, data := range rr.Data { for i, data := range rr.Data {
dataBytes[i] = data.ToWireFormat() dataBytes[i] = data.ToWireFormat()
length = length + uint32(len(dataBytes[i])) length += uint32(len(dataBytes[i]))
} }
b := make([]byte, length) b := make([]byte, length)
native.PutUint32(b[0:4], length) native.PutUint32(b[0:4], length)
@ -180,27 +234,29 @@ func (rr *NetlinkRequest) ToWireFormat() []byte {
native.PutUint32(b[8:12], rr.Seq) native.PutUint32(b[8:12], rr.Seq)
native.PutUint32(b[12:16], rr.Pid) native.PutUint32(b[12:16], rr.Pid)
i := 16 next := 16
for _, data := range dataBytes { for _, data := range dataBytes {
for _, dataByte := range data { copy(b[next:], data)
b[i] = dataByte next += len(data)
i = i + 1
}
} }
return b return b
} }
func (rr *NetlinkRequest) AddData(data NetlinkRequestData) { func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
if data != nil {
rr.Data = append(rr.Data, data) rr.Data = append(rr.Data, data)
} }
}
func newNetlinkRequest(proto, flags int) *NetlinkRequest { func newNetlinkRequest(proto, flags int) *NetlinkRequest {
rr := &NetlinkRequest{} return &NetlinkRequest{
rr.Len = uint32(syscall.NLMSG_HDRLEN) NlMsghdr: syscall.NlMsghdr{
rr.Type = uint16(proto) Len: uint32(syscall.NLMSG_HDRLEN),
rr.Flags = syscall.NLM_F_REQUEST | uint16(flags) Type: uint16(proto),
rr.Seq = uint32(getSeq()) Flags: syscall.NLM_F_REQUEST | uint16(flags),
return rr Seq: uint32(getSeq()),
},
}
} }
type NetlinkSocket struct { type NetlinkSocket struct {
@ -243,7 +299,7 @@ func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
return nil, err return nil, err
} }
if nr < syscall.NLMSG_HDRLEN { if nr < syscall.NLMSG_HDRLEN {
return nil, fmt.Errorf("Got short response from netlink") return nil, ErrShortResponse
} }
rb = rb[:nr] rb = rb[:nr]
return syscall.ParseNetlinkMessage(rb) return syscall.ParseNetlinkMessage(rb)
@ -258,7 +314,7 @@ func (s *NetlinkSocket) GetPid() (uint32, error) {
case *syscall.SockaddrNetlink: case *syscall.SockaddrNetlink:
return v.Pid, nil return v.Pid, nil
} }
return 0, fmt.Errorf("Wrong socket type") return 0, ErrWrongSockType
} }
func (s *NetlinkSocket) HandleAck(seq uint32) error { func (s *NetlinkSocket) HandleAck(seq uint32) error {
@ -355,6 +411,28 @@ func NetworkLinkUp(iface *net.Interface) error {
return s.HandleAck(wb.Seq) return s.HandleAck(wb.Seq)
} }
func NetworkLinkDown(iface *net.Interface) error {
s, err := getNetlinkSocket()
if err != nil {
return err
}
defer s.Close()
wb := newNetlinkRequest(syscall.RTM_NEWLINK, syscall.NLM_F_ACK)
msg := newIfInfomsg(syscall.AF_UNSPEC)
msg.Change = syscall.IFF_UP
msg.Flags = 0 & ^syscall.IFF_UP
msg.Index = int32(iface.Index)
wb.AddData(msg)
if err := s.Send(wb); err != nil {
return err
}
return s.HandleAck(wb.Seq)
}
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 {
@ -368,7 +446,7 @@ func NetworkSetMTU(iface *net.Interface, mtu int) error {
msg.Type = syscall.RTM_SETLINK msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(iface.Index) msg.Index = int32(iface.Index)
msg.Change = 0xFFFFFFFF msg.Change = DEFAULT_CHANGE
wb.AddData(msg) wb.AddData(msg)
var ( var (
@ -386,6 +464,103 @@ 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 NetworkSetMaster(iface, master *net.Interface) error {
s, err := getNetlinkSocket()
if err != nil {
return err
}
defer s.Close()
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)
var (
b = make([]byte, 4)
native = nativeEndian()
)
native.PutUint32(b, uint32(master.Index))
data := newRtAttr(syscall.IFLA_MASTER, b)
wb.AddData(data)
if err := s.Send(wb); err != nil {
return err
}
return s.HandleAck(wb.Seq)
}
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
s, err := getNetlinkSocket()
if err != nil {
return err
}
defer s.Close()
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)
var (
b = make([]byte, 4)
native = nativeEndian()
)
native.PutUint32(b, uint32(nspid))
data := newRtAttr(syscall.IFLA_NET_NS_PID, b)
wb.AddData(data)
if err := s.Send(wb); err != nil {
return err
}
return s.HandleAck(wb.Seq)
}
func NetworkSetNsFd(iface *net.Interface, fd int) error {
s, err := getNetlinkSocket()
if err != nil {
return err
}
defer s.Close()
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)
var (
b = make([]byte, 4)
native = nativeEndian()
)
native.PutUint32(b, uint32(fd))
data := newRtAttr(IFLA_NET_NS_FD, b)
wb.AddData(data)
if err := s.Send(wb); err != nil {
return err
}
return s.HandleAck(wb.Seq)
}
// Add an Ip address to an interface. This is identical to: // Add an Ip address to an interface. This is identical to:
// ip addr add $ip/$ipNet dev $iface // ip addr add $ip/$ipNet dev $iface
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
@ -426,20 +601,11 @@ func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
} }
func zeroTerminated(s string) []byte { func zeroTerminated(s string) []byte {
bytes := make([]byte, len(s)+1) return []byte(s + "\000")
for i := 0; i < len(s); i++ {
bytes[i] = s[i]
}
bytes[len(s)] = 0
return bytes
} }
func nonZeroTerminated(s string) []byte { func nonZeroTerminated(s string) []byte {
bytes := make([]byte, len(s)) return []byte(s)
for i := 0; i < len(s); i++ {
bytes[i] = s[i]
}
return bytes
} }
// Add a new network link of a specified type. This is identical to // Add a new network link of a specified type. This is identical to
@ -456,10 +622,10 @@ func NetworkLinkAdd(name string, linkType string) error {
msg := newIfInfomsg(syscall.AF_UNSPEC) msg := newIfInfomsg(syscall.AF_UNSPEC)
wb.AddData(msg) wb.AddData(msg)
if name != "" {
nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name)) nameData := newRtAttr(syscall.IFLA_IFNAME, zeroTerminated(name))
wb.AddData(nameData) wb.AddData(nameData)
}
IFLA_INFO_KIND := 1
kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType)) kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
@ -576,3 +742,69 @@ done:
return res, nil return res, nil
} }
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
}
func NetworkChangeName(iface *net.Interface, newName string) error {
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)
}

View File

@ -3,31 +3,59 @@
package netlink package netlink
import ( import (
"fmt" "errors"
"net" "net"
) )
var (
ErrNotImplemented = errors.New("not implemented")
)
func NetworkGetRoutes() ([]Route, error) { func NetworkGetRoutes() ([]Route, error) {
return nil, fmt.Errorf("Not implemented") return nil, ErrNotImplemented
} }
func NetworkLinkAdd(name string, linkType string) error { func NetworkLinkAdd(name string, linkType string) error {
return fmt.Errorf("Not implemented") return ErrNotImplemented
} }
func NetworkLinkUp(iface *net.Interface) error { func NetworkLinkUp(iface *net.Interface) error {
return fmt.Errorf("Not implemented") return ErrNotImplemented
} }
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error { func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
return fmt.Errorf("Not implemented") return ErrNotImplemented
} }
func AddDefaultGw(ip net.IP) error { func AddDefaultGw(ip net.IP) error {
return fmt.Errorf("Not implemented") return ErrNotImplemented
} }
func NetworkSetMTU(iface *net.Interface, mtu int) error { func NetworkSetMTU(iface *net.Interface, mtu int) error {
return fmt.Errorf("Not implemented") return ErrNotImplemented
}
func NetworkCreateVethPair(name1, name2 string) error {
return ErrNotImplemented
}
func NetworkChangeName(iface *net.Interface, newName string) error {
return ErrNotImplemented
}
func NetworkSetNsFd(iface *net.Interface, fd int) error {
return ErrNotImplemented
}
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
return ErrNotImplemented
}
func NetworkSetMaster(iface, master *net.Interface) error {
return ErrNotImplemented
}
func NetworkLinkDown(iface *net.Interface) error {
return ErrNotImplemented
} }