Add routes using AF_ROUTE in golang on Mac

Viewed 279

I am trying to add route in my Mac machine using AF_ROUTE socket in golang. I took one pragram in C and trying to convert it to golang. below is my golang program:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "syscall"
    "unsafe"
)

/* Copied and Converted from
https://unix.superglobalmegacorp.com/Net2/newsrc/net/route.h.html
*/

type rt_metrics struct {
    rmx_locks    uint64    /* Kernel must leave these values alone */
    rmx_mtu      uint64    /* MTU for this path */
    rmx_hopcount uint64    /* max hops expected */
    rmx_expire   uint64    /* lifetime for route, e.g. redirect */
    rmx_recvpipe uint64    /* inbound delay-bandwith product */
    rmx_sendpipe uint64    /* outbound delay-bandwith product */
    rmx_ssthresh uint64    /* outbound gateway buffer limit */
    rmx_rtt      uint64    /* estimated round trip time */
    rmx_rttvar   uint64    /* estimated rtt variance */
    rmx_pksent   uint32    /* packets sent using this route */
    rmx_state    uint32    /* route state */
    rmx_filler   [3]uint32 /* will be used for T/TCP later */
}

/* Copied and Converted from
https://unix.superglobalmegacorp.com/Net2/newsrc/net/route.h.html
*/

type rt_msghdr struct {
    rtm_msglen  uint16     /* to skip over non-understood messages */
    rtm_version uint8      /* future binary compatability */
    rtm_type    uint8      /* message type */
    rtm_index   uint16     /* index for associated ifp */
    rtm_pid     uint32     /* identify sender */
    rtm_addrs   int        /* bitmask identifying sockaddrs in msg */
    rtm_seq     int        /* for sender to identify action */
    rtm_errno   int        /* why failed */
    rtm_flags   int        /* flags, incl. kern & message, e.g. DONE */
    rtm_use     int        /* from rtentry */
    rtm_inits   uint64     /* which metrics we are initializing */
    rtm_rmx     rt_metrics /* metrics themselves */
}

type rt_msg struct {
    hdr   rt_msghdr
    addr1 syscall.RawSockaddrInet4
    addr2 syscall.RawSockaddrInet4
}

//Init initialize ROUTE Socket
func main() {
    address1 := [4]byte{12, 13, 14, 15}
    address2 := [4]byte{16, 17, 18, 19}
    zero1 := [8]int8{0, 0, 0, 0, 0, 0, 0, 0}
    addr1 := syscall.RawSockaddrInet4{
        0,
        syscall.AF_INET,
        0,
        address1,
        zero1,
    }
    addr2 := syscall.RawSockaddrInet4{
        0,
        syscall.AF_INET,
        0,
        address2,
        zero1,
    }
    var dummy rt_msghdr
    a := unsafe.Sizeof(dummy)
    b := unsafe.Sizeof(addr1) + unsafe.Sizeof(addr1)
    c := a + b
    //  fmt.Print(" a  ", a)
    //  fmt.Print(" b  ", b)
    //  fmt.Print(" c  ", c)
    msgheader := rt_msghdr{
        uint16(c),
        syscall.RTM_VERSION,
        syscall.RTM_ADD,
        (syscall.RTA_DST | syscall.RTA_GATEWAY),
        uint32(syscall.Getpid()),
        syscall.RTA_DST | syscall.RTA_GATEWAY,
        1,
        0,
        0,
        0,
        0,
        rt_metrics{},
    }

    msg := rt_msg{msgheader, addr1, addr2}

    fd, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC)
    if err != nil {
        fmt.Printf("Could not create Raw Socket")
    }
    defer func() {
        recover()
    }()
    msgtosend := new(bytes.Buffer)
    json.NewEncoder(msgtosend).Encode(msg)
    fmt.Print(" lenght of data ", len(msgtosend.Bytes()))
    d, e := syscall.Write(fd, msgtosend.Bytes())
    fmt.Print(" written ", d, " error is ", e, "\n\n")
}

in this when i am running this program it says buffer is not available. could anyone help what could be the issue here. Could be my structs are not correct or something basic might be missing.

1 Answers

You mentioned "Could be your struct are not correct?", Yes.. Make the names of the fields exported and use struct tags on these fields, also for rt_msghdr to make the encoder know the correct names.

type rt_msg struct {
    Hdr   rt_msghdr                   `json:"hdr"`
    Addr1 syscall.RawSockaddrInet4    `json:"addr1"`
    Addr2 syscall.RawSockaddrInet4    `json:"addr2"`
}

type rt_msghdr struct {
    Rtm_msglen  uint16 `json:"rtm_msglen"`    /* to skip over non-understood messages */
    // ... and so on
    Rtm_rmx     rt_metrics `json:"rtm_rmx"` /* metrics themselves */
}

type rt_metrics struct {
    Rmx_locks    uint64    `json:"rmx_locks"` /* Kernel must leave these values alone */
    // ... and so on
}

Code above fixed the issue for empty bytes send to fd. for no buffer space available you can check this link: https://docs.netgate.com/pfsense/en/latest/routing/no-buffer-space-available.html . compiling your code might get some issue's on other OS because it use syscall. some flag may not supported.

Related