5

I am looking for a way of sending packets using the go language which grants me the ability to view and set the TTL and other low level fields of packets(I don't need to modify Ethernet headers just IP & UDP headers). I have attempted to craft and send my own packets in the code below.

When running the below code no messages are received by the receiver. Can anyone either a.) alert me to any errors I have made in this code or b.) suggest another means of sending UDP data using golang while retaining access to the IP & UDP headers?

I have removed the MAC addresses, they would need to be replaced to run this code.

(Sending)

package main
    
import (
    "syscall"
    "log"
    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "fmt"
    "encoding/hex"
    "encoding/json"
    "net"
)

type NameTag struct {
    Name string `json:"name"`
}

func main() {
    var err error
    fd, e := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_UDP)
    if e != nil {
        fmt.Println("Problem @ location 1")
    }
    addr := syscall.SockaddrInet4{
        Port: 27289,
        Addr: [4]byte{127, 0, 0, 1},
    }
    p := pkt()
    err = syscall.Sendto(fd, p, 0, &addr)
    if err != nil {
        log.Fatal("Sendto:", err)
    }
}

func pkt() []byte {
    sb := gopacket.NewSerializeBuffer()
    nt := NameTag{"Paul"}
    b, _ := json.Marshal(nt)
    pld := gopacket.Payload(b)
    l := uint16(len(pld))
    udp := layers.UDP{
        SrcPort:  27289,
        DstPort:  27288,
        Length:   l + 8,
        Checksum: 0,
    }
    l = l + 8
    ip := layers.IPv4{
        Version:    0x4,
        IHL:        5,
        Length:     20 + l,
        TTL:        255,
        Flags:      0x40,
        FragOffset: 0,
        Checksum:   0,
        Protocol:   syscall.IPPROTO_UDP,
        DstIP:      net.IPv4(127, 0, 0, 1),
        SrcIP:      net.IPv4(127, 0, 0, 1),
    }
    l = l + 20

    eth := layers.Ethernet{
        EthernetType: layers.EthernetTypeIPv4,
        SrcMAC: net.HardwareAddr{
            0x--, 0x--, 0x--, 0x--, 0x--, 0x--,
        },
        DstMAC: net.HardwareAddr{
            0x--, 0x--, 0x--, 0x--, 0x--, 0x--,
        },
    }
    fmt.Println(pld.SerializeTo(sb, gopacket.SerializeOptions{}))
    fmt.Println(udp.SerializeTo(sb, gopacket.SerializeOptions{}))
    fmt.Println(ip.SerializeTo(sb, gopacket.SerializeOptions{}))
    fmt.Println(eth.SerializeTo(sb, gopacket.SerializeOptions{}))

    //Debug prints here (first line is for dump to packet validator)
    fmt.Println(hex.EncodeToString(sb.Bytes()))
    fmt.Println(sb.Bytes())

    return sb.Bytes()
}

(Receiving)

package main
    
import (
    "syscall"
    "os"
    "fmt"
)

func main() {
    fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_UDP)
    if err != nil {
        fmt.Println(err)
        return
    }
    sa:= &syscall.SockaddrInet4{
        Addr: [4]byte{127,0,0,1},
        Port:27288,
    }
    e := syscall.Bind(fd, sa)
    if e != nil {
        fmt.Println("problems @ location 1")
    }
    f := os.NewFile(uintptr(fd), fmt.Sprintf("fd%d", fd))
    fmt.Println("Entering main loop")
    for {
        fmt.Println("In loop")
        buf := make([]byte, 1024)
        numRead, err := f.Read(buf)
        if err != nil {
            fmt.Println("problems @ location 2")
        }
        fmt.Printf("Loop done %v\n", buf[:numRead])
    }

}
FObersteiner
  • 22,500
  • 8
  • 42
  • 72
Brash Man
  • 81
  • 4
  • why syscall? _"The primary use of syscall is inside other packages that provide a more portable interface to the system, such as "os", "time" and "net". Use those packages rather than this one if you can."_ – vitr Apr 26 '18 at 02:55
  • its working for me, all I did was build the binary and run with sudo permission. – whitespace Apr 26 '18 at 04:25
  • Interesting, what OS / Architecture are you running with? – Brash Man Apr 26 '18 at 23:52
  • @vitr is there a way of getting access to the ip & udp headers while using the "net" package? if so that is preferable but I couldn't figure out how to do that. – Brash Man Apr 27 '18 at 00:00
  • It worked for me when I removed the `Ethernet` layer altogether from the `SerializeBuffer`, since I also need to only modify IP & UDP headers. Just a question, why do you need to use a raw socket on the receiver, shouldn't it be able to read the data through a regular socket as well? – Utku Ufuk Feb 11 '20 at 21:09

0 Answers0