Move utility package 'netlink' to pkg/netlink
This commit is contained in:
parent
81b755db9b
commit
38e2bb05e9
2 changed files with 609 additions and 0 deletions
31
netlink/netlink_darwin.go
Normal file
31
netlink/netlink_darwin.go
Normal file
|
@ -0,0 +1,31 @@
|
|||
package netlink
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func NetworkGetRoutes() ([]*net.IPNet, error) {
|
||||
return nil, fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func NetworkLinkAdd(name string, linkType string) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func NetworkLinkUp(iface *net.Interface) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
||||
|
||||
func AddDefaultGw(ip net.IP) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
|
||||
}
|
||||
|
||||
func NetworkSetMTU(iface *net.Interface, mtu int) error {
|
||||
return fmt.Errorf("Not implemented")
|
||||
}
|
578
netlink/netlink_linux.go
Normal file
578
netlink/netlink_linux.go
Normal file
|
@ -0,0 +1,578 @@
|
|||
package netlink
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var nextSeqNr int
|
||||
|
||||
func nativeEndian() binary.ByteOrder {
|
||||
var x uint32 = 0x01020304
|
||||
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||
return binary.BigEndian
|
||||
}
|
||||
return binary.LittleEndian
|
||||
}
|
||||
|
||||
func getSeq() int {
|
||||
nextSeqNr = nextSeqNr + 1
|
||||
return nextSeqNr
|
||||
}
|
||||
|
||||
func getIpFamily(ip net.IP) int {
|
||||
if len(ip) <= net.IPv4len {
|
||||
return syscall.AF_INET
|
||||
}
|
||||
if ip.To4() != nil {
|
||||
return syscall.AF_INET
|
||||
}
|
||||
return syscall.AF_INET6
|
||||
}
|
||||
|
||||
type NetlinkRequestData interface {
|
||||
ToWireFormat() []byte
|
||||
}
|
||||
|
||||
type IfInfomsg struct {
|
||||
syscall.IfInfomsg
|
||||
}
|
||||
|
||||
func newIfInfomsg(family int) *IfInfomsg {
|
||||
msg := &IfInfomsg{}
|
||||
msg.Family = uint8(family)
|
||||
msg.Type = uint16(0)
|
||||
msg.Index = int32(0)
|
||||
msg.Flags = uint32(0)
|
||||
msg.Change = uint32(0)
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *IfInfomsg) ToWireFormat() []byte {
|
||||
native := nativeEndian()
|
||||
|
||||
len := syscall.SizeofIfInfomsg
|
||||
b := make([]byte, len)
|
||||
b[0] = msg.Family
|
||||
b[1] = 0
|
||||
native.PutUint16(b[2:4], msg.Type)
|
||||
native.PutUint32(b[4:8], uint32(msg.Index))
|
||||
native.PutUint32(b[8:12], msg.Flags)
|
||||
native.PutUint32(b[12:16], msg.Change)
|
||||
return b
|
||||
}
|
||||
|
||||
type IfAddrmsg struct {
|
||||
syscall.IfAddrmsg
|
||||
}
|
||||
|
||||
func newIfAddrmsg(family int) *IfAddrmsg {
|
||||
msg := &IfAddrmsg{}
|
||||
msg.Family = uint8(family)
|
||||
msg.Prefixlen = uint8(0)
|
||||
msg.Flags = uint8(0)
|
||||
msg.Scope = uint8(0)
|
||||
msg.Index = uint32(0)
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *IfAddrmsg) ToWireFormat() []byte {
|
||||
native := nativeEndian()
|
||||
|
||||
len := syscall.SizeofIfAddrmsg
|
||||
b := make([]byte, len)
|
||||
b[0] = msg.Family
|
||||
b[1] = msg.Prefixlen
|
||||
b[2] = msg.Flags
|
||||
b[3] = msg.Scope
|
||||
native.PutUint32(b[4:8], msg.Index)
|
||||
return b
|
||||
}
|
||||
|
||||
type RtMsg struct {
|
||||
syscall.RtMsg
|
||||
}
|
||||
|
||||
func newRtMsg(family int) *RtMsg {
|
||||
msg := &RtMsg{}
|
||||
msg.Family = uint8(family)
|
||||
msg.Table = syscall.RT_TABLE_MAIN
|
||||
msg.Scope = syscall.RT_SCOPE_UNIVERSE
|
||||
msg.Protocol = syscall.RTPROT_BOOT
|
||||
msg.Type = syscall.RTN_UNICAST
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
func (msg *RtMsg) ToWireFormat() []byte {
|
||||
native := nativeEndian()
|
||||
|
||||
len := syscall.SizeofRtMsg
|
||||
b := make([]byte, len)
|
||||
b[0] = msg.Family
|
||||
b[1] = msg.Dst_len
|
||||
b[2] = msg.Src_len
|
||||
b[3] = msg.Tos
|
||||
b[4] = msg.Table
|
||||
b[5] = msg.Protocol
|
||||
b[6] = msg.Scope
|
||||
b[7] = msg.Type
|
||||
native.PutUint32(b[8:12], msg.Flags)
|
||||
return b
|
||||
}
|
||||
|
||||
func rtaAlignOf(attrlen int) int {
|
||||
return (attrlen + syscall.RTA_ALIGNTO - 1) & ^(syscall.RTA_ALIGNTO - 1)
|
||||
}
|
||||
|
||||
type RtAttr struct {
|
||||
syscall.RtAttr
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func newRtAttr(attrType int, data []byte) *RtAttr {
|
||||
attr := &RtAttr{}
|
||||
attr.Type = uint16(attrType)
|
||||
attr.Data = data
|
||||
|
||||
return attr
|
||||
}
|
||||
|
||||
func (attr *RtAttr) ToWireFormat() []byte {
|
||||
native := nativeEndian()
|
||||
|
||||
len := syscall.SizeofRtAttr + len(attr.Data)
|
||||
b := make([]byte, rtaAlignOf(len))
|
||||
native.PutUint16(b[0:2], uint16(len))
|
||||
native.PutUint16(b[2:4], attr.Type)
|
||||
for i, d := range attr.Data {
|
||||
b[4+i] = d
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
type NetlinkRequest struct {
|
||||
syscall.NlMsghdr
|
||||
Data []NetlinkRequestData
|
||||
}
|
||||
|
||||
func (rr *NetlinkRequest) ToWireFormat() []byte {
|
||||
native := nativeEndian()
|
||||
|
||||
length := rr.Len
|
||||
dataBytes := make([][]byte, len(rr.Data))
|
||||
for i, data := range rr.Data {
|
||||
dataBytes[i] = data.ToWireFormat()
|
||||
length = length + uint32(len(dataBytes[i]))
|
||||
}
|
||||
b := make([]byte, length)
|
||||
native.PutUint32(b[0:4], length)
|
||||
native.PutUint16(b[4:6], rr.Type)
|
||||
native.PutUint16(b[6:8], rr.Flags)
|
||||
native.PutUint32(b[8:12], rr.Seq)
|
||||
native.PutUint32(b[12:16], rr.Pid)
|
||||
|
||||
i := 16
|
||||
for _, data := range dataBytes {
|
||||
for _, dataByte := range data {
|
||||
b[i] = dataByte
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (rr *NetlinkRequest) AddData(data NetlinkRequestData) {
|
||||
rr.Data = append(rr.Data, data)
|
||||
}
|
||||
|
||||
func newNetlinkRequest(proto, flags int) *NetlinkRequest {
|
||||
rr := &NetlinkRequest{}
|
||||
rr.Len = uint32(syscall.NLMSG_HDRLEN)
|
||||
rr.Type = uint16(proto)
|
||||
rr.Flags = syscall.NLM_F_REQUEST | uint16(flags)
|
||||
rr.Seq = uint32(getSeq())
|
||||
return rr
|
||||
}
|
||||
|
||||
type NetlinkSocket struct {
|
||||
fd int
|
||||
lsa syscall.SockaddrNetlink
|
||||
}
|
||||
|
||||
func getNetlinkSocket() (*NetlinkSocket, error) {
|
||||
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &NetlinkSocket{
|
||||
fd: fd,
|
||||
}
|
||||
s.lsa.Family = syscall.AF_NETLINK
|
||||
if err := syscall.Bind(fd, &s.lsa); err != nil {
|
||||
syscall.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) Close() {
|
||||
syscall.Close(s.fd)
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
|
||||
if err := syscall.Sendto(s.fd, request.ToWireFormat(), 0, &s.lsa); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
|
||||
rb := make([]byte, syscall.Getpagesize())
|
||||
nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if nr < syscall.NLMSG_HDRLEN {
|
||||
return nil, fmt.Errorf("Got short response from netlink")
|
||||
}
|
||||
rb = rb[:nr]
|
||||
return syscall.ParseNetlinkMessage(rb)
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) GetPid() (uint32, error) {
|
||||
lsa, err := syscall.Getsockname(s.fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch v := lsa.(type) {
|
||||
case *syscall.SockaddrNetlink:
|
||||
return v.Pid, nil
|
||||
}
|
||||
return 0, fmt.Errorf("Wrong socket type")
|
||||
}
|
||||
|
||||
func (s *NetlinkSocket) HandleAck(seq uint32) error {
|
||||
native := nativeEndian()
|
||||
|
||||
pid, err := s.GetPid()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
done:
|
||||
for {
|
||||
msgs, err := s.Recieve()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, m := range msgs {
|
||||
if m.Header.Seq != seq {
|
||||
return fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, seq)
|
||||
}
|
||||
if m.Header.Pid != pid {
|
||||
return fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
||||
}
|
||||
if m.Header.Type == syscall.NLMSG_DONE {
|
||||
break done
|
||||
}
|
||||
if m.Header.Type == syscall.NLMSG_ERROR {
|
||||
error := int32(native.Uint32(m.Data[0:4]))
|
||||
if error == 0 {
|
||||
break done
|
||||
}
|
||||
return syscall.Errno(-error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add a new default gateway. Identical to:
|
||||
// ip route add default via $ip
|
||||
func AddDefaultGw(ip net.IP) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
family := getIpFamily(ip)
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
msg := newRtMsg(family)
|
||||
wb.AddData(msg)
|
||||
|
||||
var ipData []byte
|
||||
if family == syscall.AF_INET {
|
||||
ipData = ip.To4()
|
||||
} else {
|
||||
ipData = ip.To16()
|
||||
}
|
||||
|
||||
gateway := newRtAttr(syscall.RTA_GATEWAY, ipData)
|
||||
|
||||
wb.AddData(gateway)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
// Bring up a particular network interface
|
||||
func NetworkLinkUp(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 = 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 {
|
||||
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 = 0xFFFFFFFF
|
||||
wb.AddData(msg)
|
||||
|
||||
var (
|
||||
b = make([]byte, 4)
|
||||
native = nativeEndian()
|
||||
)
|
||||
native.PutUint32(b, uint32(mtu))
|
||||
|
||||
data := newRtAttr(syscall.IFLA_MTU, 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:
|
||||
// ip addr add $ip/$ipNet dev $iface
|
||||
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
family := getIpFamily(ip)
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_NEWADDR, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
||||
|
||||
msg := newIfAddrmsg(family)
|
||||
msg.Index = uint32(iface.Index)
|
||||
prefixLen, _ := ipNet.Mask.Size()
|
||||
msg.Prefixlen = uint8(prefixLen)
|
||||
wb.AddData(msg)
|
||||
|
||||
var ipData []byte
|
||||
if family == syscall.AF_INET {
|
||||
ipData = ip.To4()
|
||||
} else {
|
||||
ipData = ip.To16()
|
||||
}
|
||||
|
||||
localData := newRtAttr(syscall.IFA_LOCAL, ipData)
|
||||
wb.AddData(localData)
|
||||
|
||||
addrData := newRtAttr(syscall.IFA_ADDRESS, ipData)
|
||||
wb.AddData(addrData)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.HandleAck(wb.Seq)
|
||||
}
|
||||
|
||||
func zeroTerminated(s string) []byte {
|
||||
bytes := make([]byte, len(s)+1)
|
||||
for i := 0; i < len(s); i++ {
|
||||
bytes[i] = s[i]
|
||||
}
|
||||
bytes[len(s)] = 0
|
||||
return bytes
|
||||
}
|
||||
|
||||
func nonZeroTerminated(s string) []byte {
|
||||
bytes := make([]byte, len(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
|
||||
// running: ip add link $name type $linkType
|
||||
func NetworkLinkAdd(name string, linkType 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(name))
|
||||
wb.AddData(nameData)
|
||||
|
||||
IFLA_INFO_KIND := 1
|
||||
|
||||
kindData := newRtAttr(IFLA_INFO_KIND, nonZeroTerminated(linkType))
|
||||
|
||||
infoData := newRtAttr(syscall.IFLA_LINKINFO, kindData.ToWireFormat())
|
||||
wb.AddData(infoData)
|
||||
|
||||
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
|
||||
// This is similar to the first column of "ip route" output
|
||||
func NetworkGetRoutes() ([]*net.IPNet, error) {
|
||||
native := nativeEndian()
|
||||
|
||||
s, err := getNetlinkSocket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
wb := newNetlinkRequest(syscall.RTM_GETROUTE, syscall.NLM_F_DUMP)
|
||||
|
||||
msg := newIfInfomsg(syscall.AF_UNSPEC)
|
||||
wb.AddData(msg)
|
||||
|
||||
if err := s.Send(wb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pid, err := s.GetPid()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]*net.IPNet, 0)
|
||||
|
||||
done:
|
||||
for {
|
||||
msgs, err := s.Recieve()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, m := range msgs {
|
||||
if m.Header.Seq != wb.Seq {
|
||||
return nil, fmt.Errorf("Wrong Seq nr %d, expected 1", m.Header.Seq)
|
||||
}
|
||||
if m.Header.Pid != pid {
|
||||
return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)
|
||||
}
|
||||
if m.Header.Type == syscall.NLMSG_DONE {
|
||||
break done
|
||||
}
|
||||
if m.Header.Type == syscall.NLMSG_ERROR {
|
||||
error := int32(native.Uint32(m.Data[0:4]))
|
||||
if error == 0 {
|
||||
break done
|
||||
}
|
||||
return nil, syscall.Errno(-error)
|
||||
}
|
||||
if m.Header.Type != syscall.RTM_NEWROUTE {
|
||||
continue
|
||||
}
|
||||
|
||||
var iface *net.Interface = nil
|
||||
var ipNet *net.IPNet = nil
|
||||
|
||||
msg := (*RtMsg)(unsafe.Pointer(&m.Data[0:syscall.SizeofRtMsg][0]))
|
||||
|
||||
if msg.Flags&syscall.RTM_F_CLONED != 0 {
|
||||
// Ignore cloned routes
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Table != syscall.RT_TABLE_MAIN {
|
||||
// Ignore non-main tables
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Family != syscall.AF_INET {
|
||||
// Ignore non-ipv4 routes
|
||||
continue
|
||||
}
|
||||
|
||||
if msg.Dst_len == 0 {
|
||||
// Ignore default routes
|
||||
continue
|
||||
}
|
||||
|
||||
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, attr := range attrs {
|
||||
switch attr.Attr.Type {
|
||||
case syscall.RTA_DST:
|
||||
ip := attr.Value
|
||||
ipNet = &net.IPNet{
|
||||
IP: ip,
|
||||
Mask: net.CIDRMask(int(msg.Dst_len), 8*len(ip)),
|
||||
}
|
||||
case syscall.RTA_OIF:
|
||||
index := int(native.Uint32(attr.Value[0:4]))
|
||||
iface, _ = net.InterfaceByIndex(index)
|
||||
_ = iface
|
||||
}
|
||||
}
|
||||
if ipNet != nil {
|
||||
res = append(res, ipNet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
Loading…
Reference in a new issue