Reading TCP packets via raw sockets in GO - sockets

I'm researching raw sockets in GO. I would like to be able to read all TCP packets going to my computer (OSX, en0: 192.168.1.65)
If I switch the protocol from tcp to icmp, I will get packets. Why do I have no packets being read with my code?
package main
import (
"fmt"
"net"
)
func main() {
netaddr, err := net.ResolveIPAddr("ip4", "192.168.1.65")
if err != nil {
fmt.Println(err)
}
conn, err := net.ListenIP("ip4:tcp", netaddr)
if err != nil {
fmt.Println(err)
}
buf := make([]byte, 2048)
for {
numRead, recvAddr, err := conn.ReadFrom(buf)
if err != nil {
fmt.Println(err)
}
if recvAddr != nil {
fmt.Println(recvAddr)
}
s := string(buf[:numRead])
fmt.Println(s)
}
}

The problem with this is that OS X is based on BSD, and BSD doesn't allow you to program raw sockets at the TCP level. You have to use go down to the Ethernet level in order to do so.
I'm using the pcap library with gopackets to do the job.
https://godoc.org/code.google.com/p/gopacket/pcap

Related

How to get MAC address of Ethernet interface of a UDP socket?

I want to create a UDP server.
The server has several network interfaces : I want each interface to respond even if it is not in the same subnet as clients (clients send broadcast Ethernet packets).
How to do it ?
// The UDP server receives the client's request but fails to respond if it is not in the same subnet mask.
func udp_server(local_interface_ip net.IP){
p := make([]byte, 2048)
addr := net.UDPAddr{
Port: 1234,
IP: local_interface_ip,
}
ser, err := net.ListenUDP("udp", &addr)
if err != nil {
fmt.Printf("Some error %v\r\n", err)
return
}
for {
packetLen, remoteaddr, err := ser.ReadFromUDP(p)
if err != nil {
fmt.Printf("Some error %v", err)
continue
}
fmt.Printf("[RX %v] message(len : %d) : %s\r\n", remoteaddr, packetLen, p)
sendResponse(ser, remoteaddr)
}
}
func sendResponse(conn *net.UDPConn, addr *net.UDPAddr) {
data := []byte("1234")
_, err := conn.WriteToUDP(data, addr)
if err != nil {
fmt.Printf("[TX] Couldn't send response to %v (%v)\r\n", addr, err)
}
}
Then, for the server response, I want to put in the UDP data the MAC address of the server interface: How to do?
// This function get all MAC Address of the server but to know what is the good interface ?
func getMacAddr() ([]string, error) {
ifas, err := net.Interfaces()
if err != nil {
return nil, err
}
var as []string
for _, ifa := range ifas {
a := ifa.HardwareAddr.String()
if a != "" {
as = append(as, a)
}
}
return as, nil
}
It is a custom protocol based on UDP which is imposed to me which allows the clients to be able to discover IP address and MAC address of server interfaces.

How to listen at the data link layer (ethernet) and respond at the transport layer

What I am trying to do is listen to ethernet frames for IPv6 and respond to UDP calls on a specific port.
I am able to capture the ethernet frames I care about and parse out the UDP payload, but when I attempt to echo that payload back is where I have a problem. Here is my "server" code:
func main() {
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_IPV6)))
iface, err := net.InterfaceByName("lo")
if err != nil {
log.Fatal(err)
}
err = syscall.BindToDevice(fd, iface.Name)
if err != nil {
log.Fatal(err)
}
for {
buf := make([]byte, iface.MTU)
n, callerAddr, err := syscall.Recvfrom(fd, buf, 0)
if err != nil {
log.Fatal(err)
}
data := buf[:n]
packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
udpPacket := packet.Layer(layers.LayerTypeUDP)
if udpPacket != nil {
udpPck, _ := udpPacket.(*layers.UDP)
// I only care about calls to 8080 for this example
if udpPck.DstPort != 8080 {
continue
}
err = udpPck.SetNetworkLayerForChecksum(packet.NetworkLayer()); if err != nil {
log.Fatal(err)
}
log.Print(packet)
log.Printf("UDP Port from %v --> %v", udpPck.SrcPort, udpPck.DstPort)
log.Printf("Payload '%v'", string(udpPck.Payload))
// Flip the source and destination so it can go back to the caller
ogDst := udpPck.DstPort
udpPck.DstPort = udpPck.SrcPort
udpPck.SrcPort = ogDst
buffer := gopacket.NewSerializeBuffer()
options := gopacket.SerializeOptions{ComputeChecksums: true}
// Rebuild the packet with the new source and destination port
err := gopacket.SerializePacket(buffer, options, packet)
if err != nil {
log.Fatal(err)
}
log.Printf("Writing the payload back to the caller: %v", callerAddr)
log.Print(packet)
err = syscall.Sendto(fd, buffer.Bytes(), 0, callerAddr)
if err != nil {
log.Fatal(err)
}
}
}
And then my client code which is running on the same machine:
func main() {
conn, err := net.DialUDP("udp6", &net.UDPAddr{
IP: net.IPv6loopback,
Port: 0,
}, &net.UDPAddr{
IP: net.IPv6loopback,
Port: 8080,
})
if err != nil {
log.Fatal(err)
}
_, _ = conn.Write([]byte("Hello World"))
log.Print("Waiting for response")
buf := make([]byte, 65535)
n, _, err := conn.ReadFrom(buf)
if err != nil {
log.Fatal(err)
}
log.Printf("Response message '%v'", string(buf[:n]))
}
The problem from the client side is a connection refused read udp6 [::1]:56346->[::1]:8080: recvfrom: connection refused which my guess would be coming from the linux kernel since I have not bound anything to 8080 strictly speaking.
There is data I need from the IPv6 header (not seen above) which is why I need to listen on the data link layer, but since I also need to respond to UDP requests things get a little tricky.
An option I have but don't like would be to in a separate goroutine do a standard net.ListenUDP and then block after reading data until the IPv6 header is read from the syscall socket listener, then from there responding on the udp connection. If this is my only option I will take it but I would interested to see if there is something better I could do.
I think you still need to listen on the UDP port even though you are responding by constructing a link layer frame. Otherwise the system's networking stack will respond with an ICMP message, which is what caused the "connection refused" error.
I haven't tried this but I think if you remove the IP address from the interface, it'd prevent the kernel IP stack from running on it. But then there might be ARP messages you need to deal with.
Alternatively you might try using a TUN/TAP interface, so that you have full control over what happens on it from user space.

Using os.OpenFile() instead of net.Listen()

I've dived into the call stack of both os.OpenFile and net.Listen to see if I can make a UNIX domain socket using os.OpenFile. Below is my attempt. But, after tracing both call stacks (os.OpenFile's and net.Listen's) I'm still confused. The below code doesn't read from the file, apparently, and stores the data to the filesystem.
How can I implement a UNIX domain socket using os.OpenFile?
What is the purpose of os.ModeSocket if it's not to be used with os.OpenFile to create a UNIX socket?
package main
import (
"fmt"
"log"
"os"
)
func main() {
sock, err := os.OpenFile("f.sock", os.O_RDWR|os.O_CREATE, os.ModeSocket|os.ModePerm)
defer sock.Close()
if err != nil {
log.Panic(err)
}
n, err := sock.WriteString("hello\n")
if err != nil {
fmt.Println(err)
} else {
fmt.Println(n)
}
b := make([]byte, 10)
n, err = sock.Read(b)
fmt.Println(n)
if err != nil {
fmt.Println("error reading: ", err)
}
fmt.Println(b)
}
No. OpenFile is a generalized api for opening file, use net.Listen("unixpacket", "f.sock") or net.Dial("unixpacket", "f.sock") if you wanna work with unix socket
os.ModeSocket is just a *nix registered flag for socket fd, use when you want to filter fd types

UDP Socket not reading from server in Go

I'm developing a fast dns client in go just to mess around with But I'm facing troubles at the time of reading from server responses cause it never arrives and I know it actually did because I have WireShark open and it read the packet.
Here is the code sample(8.8.8.8 is Google DNS and the hex msg is a valid DNS query):
package main
import (
"fmt"
"net"
"encoding/hex"
"bufio"
)
func CheckError(err error) {
if err != nil {
fmt.Println("Error: " , err)
}
}
func main() {
Conn, err := net.Dial("udp", "8.8.8.8:53")
CheckError(err)
defer Conn.Close()
msg, _ := hex.DecodeString("5ab9010000010000000000001072312d2d2d736e2d68357137646e65650b676f6f676c65766964656f03636f6d0000010001")
scanner := bufio.NewScanner(Conn)
buf := []byte(msg)
_, err1 := Conn.Write(buf)
if err1 != nil {
fmt.Println(msg, err1)
}
for scanner.Scan() {
fmt.Println(scanner.Bytes())
}
}
Here you have the proof that it actually arrives:
WireShark Screen Capture
I've testes reading directly from conn with:
func main() {
Conn, err := net.Dial("udp", "8.8.8.8:53")
CheckError(err)
defer Conn.Close()
msg, _ := hex.DecodeString("5ab9010000010000000000001072312d2d2d736e2d68357137646e65650b676f6f676c65766964656f03636f6d0000010001")
buf := []byte(msg)
_, err1 := Conn.Write(buf)
if err1 != nil {
fmt.Println(msg, err1)
}
Reader(Conn)
}
func Reader(conn net.Conn) {
var buf []byte
for {
conn.Read(buf)
fmt.Println(buf)
}
}
You can't use bufio around a UDP connection. UDP is not a stream oriented protocol, so you need to differentiate the individual datagrams yourself, and avoid partial reads to prevent data loss.
In order to read from an io.Reader, you must have space allocated to read into, and you need to use the bytes read value returned from the Read operation. Your example could be reduced to:
conn, err := net.Dial("udp", "8.8.8.8:53")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
msg, _ := base64.RawStdEncoding.DecodeString("WrkBAAABAAAAAAAAEHIxLS0tc24taDVxN2RuZWULZ29vZ2xldmlkZW8DY29tAAABAAE")
resp := make([]byte, 512)
conn.Write(msg)
n, err := conn.Read(resp)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%q\n", resp[:n])

golang get udp socket buffer size

I'm writing a udp client and set udp socket send buffer by SetWriteBuffer.
addr, _ := net.ResolveUDPAddr("udp", ":8089")
conn, err :=net.DialUDP("udp", nil, addr)
err =conn.SetWriteBuffer(64*1024*1024)
as above, how can I test set the value is effective or get the send buffer value after call SetWriteBuffer function.
Thank you all.
After looking at the net package code, it looks like SetWriteBuffer makes a syscall to setsockopt (for posix). There is no similar function for GetWriteBuffer. The only way i can think to do this is by making another syscall to getsockopt like so.
addr, _ := net.ResolveUDPAddr("udp", ":8089")
conn, _ := net.DialUDP("udp", nil, addr)
conn.SetWriteBuffer(10 * 1024)
fd, _ := conn.File()
value, _ := syscall.GetsockoptInt(int(fd.Fd()), syscall.SOL_SOCKET, syscall.SO_SNDBUF)
log.Println(value)
fd.Close()
conn.Close()
Check the error value returned by SetWriteBuffer. For example,
package main
import (
"log"
"net"
)
func main() {
addr, err := net.ResolveUDPAddr("udp", ":8089")
conn, err := net.DialUDP("udp", nil, addr)
err = conn.SetWriteBuffer(64 * 1024 * 1024)
if err != nil {
log.Print(err)
}
}