123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- // +build amd64
- 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) Receive() ([]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.Receive()
- 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)
- }
- // 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 = 0xFFFFFFFF
- 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)
- }
- // 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() ([]Route, 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([]Route, 0)
- done:
- for {
- msgs, err := s.Receive()
- 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 r Route
- 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 {
- // Default routes
- r.Default = true
- }
- 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
- r.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]))
- r.Iface, _ = net.InterfaceByIndex(index)
- }
- }
- if r.Default || r.IPNet != nil {
- res = append(res, r)
- }
- }
- }
- return res, nil
- }
|