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

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.

Related

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.

How to set ToS field in IP header for a TCP connection using Golang

I am trying to create a TCP server and client using Golang where I am able to set the Type of Service field in the IP header in order to prioritise different traffic flows.
The client and servers are able to communicate but I can not figure out how to set the ToS field.
I have tried using the ipv4 Golang package with the method described here: https://godoc.org/golang.org/x/net/ipv4#NewConn
A simplified server example:
func main () {
ln, err := net.Listen("tcp4", "192.168.0.20:1024")
if err != nil {
// error handling
}
defer ln.Close()
for {
c, err := ln.Accept()
if err != nil {
// error handling
}
go func(c net.Conn) {
defer c.Close()
if err := ipv4.NewConn(c).SetTOS(0x28); err != nil {
fmt.Println("Error: ", err.Error())
}
}(c)
}
And the corresponding client (also simplified)
func main () {
conn, err := net.Dial("tcp4", "192.168.0.20:1024")
if err != nil {
fmt.Println(err)
}
for {
writer := bufio.NewWriter(conn)
// Create "packet"
Data := make([]byte, 1200)
endLine := "\r\n"
//Set packetLength
length := strconv.FormatInt(int64(1200), 10)
copy(Data[0:], length)
//Set ID
idString := strconv.FormatInt(int64(1), 10)
if strings.Contains(idString, "\r") || strings.Contains(idString, "\n") || strings.Contains(idString, "\r\n") {
fmt.Println("This is gonna result in an error in the id string.")
}
idbuf := []byte(idString)
copy(Data[15:], idbuf)
//Set timestamp
timestamp0 := time.Now().UnixNano()
timestampString := strconv.FormatInt(timestamp0, 10)
if strings.Contains(timestampString, "\r") || strings.Contains(timestampString, "\n") || strings.Contains(timestampString, "\r\n") {
fmt.Println("This is gonna result in an error in the timestamp string.")
}
buf := []byte(timestampString)
copy(Data[50:], buf)
copy(Data[int(1200)-2:], endLine)
if len(Data) != int(1200) {
fmt.Println("This is also gonna be an error. Length is: ", len(Data))
}
//Send the data and flush the writer
writer.Write(Data)
writer.Flush()
}
//time.Sleep(1*time.Nanosecond)
}
I have also tried creating my own dialer with a control function that passes a syscall in order to set the socket like this:
dialer := &net.Dialer{
Timeout: 5 * time.Second,
Deadline: time.Time{},
LocalAddr: tcpAddr,
DualStack: false,
FallbackDelay: 0,
KeepAlive: 0,
Resolver: nil,
Control: highPrio,
}
func highPrio(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
// set the socket options
err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 128)
if err != nil {
log.Println("setsocketopt: ", err)
}
})
I am verifying that it does not work by inspecting the traffic with Wireshark and am using Windows 10 Pro as my OS.
I am try you ToS set method at Dial() with golang 1.15.5 and its worked:
dialer := net.Dialer{
Timeout: this.TcpWaitConnectTimeout,
}
dialer.Control = func(network, address string, c syscall.RawConn) error {
var err error
c.Control(func(fd uintptr) {
err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, 0x80)
})
return err
}
c, err := dialer.Dial("tcp", this.serverAddr)
tcpdump show me right ToS

connect mongodb by mongo-go-driver consume huge memory

I'm using mongo-go-driver, parallel connect several ports to check whether the port is listened by mongodb
go version 1.12.3
mongo-go-driver v1.0
type BaseServerStatus struct {
Host string `bson:"host"`
Version string `bson:"version"`
Process string `bson:"process"`
Pid int64 `bson:"pid"`
Uptime int64 `bson:"uptime"`
UptimeMillis int64 `bson:"uptimeMillis"`
UptimeEstimate int64 `bson:"uptimeEstimate"`
LocalTime time.Time `bson:"localTime"`
}
func GetBaseServerStatus(ip, port string) (srvStatus *BaseServerStatus, err error) {
opts := options.Client()
opts.SetDirect(true)
opts.SetServerSelectionTimeout(1 * time.Second)
opts.SetConnectTimeout(2 * time.Second)
opts.SetSocketTimeout(2 * time.Second)
opts.SetMaxConnIdleTime(1 * time.Second)
opts.SetMaxPoolSize(1)
url := fmt.Sprintf("mongodb://%s:%s/admin", ip, port)
opts.ApplyURI(url)
ctx, _ := context.WithTimeout(context.Background(), 2*time.Second)
conn, err := mongo.Connect(ctx, opts)
if err != nil {
fmt.Printf("new %s:%s mongo connection error: %v\n", ip, port, err)
return
}
defer conn.Disconnect(ctx)
err = conn.Ping(ctx, nil)
if err != nil {
fmt.Printf("ping %s:%s ping error: %v\n", ip, port, err)
return
}
sr := conn.Database("admin").RunCommand(ctx, bson.D{{"serverStatus", 1}})
if sr.Err() != nil {
fmt.Printf("get %s:%s server status error: %v\n", ip, port, sr.Err())
return
}
srvStatus = new(BaseServerStatus)
err = sr.Decode(srvStatus)
return
}
func main() {
var wg sync.WaitGroup
//ips := []string{"xxx.xxx.xxx.xxx:22"}
ips := []string{"xxx.xxx.xxx.xxx:22", "xxx.xxx.xxx.xxx:80", "xxx.xxx.xxx.xxx:7005", "xxx.xxx.xxx.xxx:7017", "xxx.xxx.xxx.xxx:7006", "xxx.xxx.xxx.xxx:7016", "xxx.xxx.xxx.xxx:7018", "xxx.xxx.xxx.xxx:7014", "xxx.xxx.xxx.xxx:199", "xxx.xxx.xxx.xxx:8182", "xxx.xxx.xxx.xxx:7015", "xxx.xxx.xxx.xxx:7022", "xxx.xxx.xxx.xxx:7013", "xxx.xxx.xxx.xxx:7020", "xxx.xxx.xxx.xxx:9009", "xxx.xxx.xxx.xxx:7004", "xxx.xxx.xxx.xxx:7008", "xxx.xxx.xxx.xxx:7002", "xxx.xxx.xxx.xxx:7021", "xxx.xxx.xxx.xxx:7007", "xxx.xxx.xxx.xxx:7024", "xxx.xxx.xxx.xxx:7010", "xxx.xxx.xxx.xxx:7011", "xxx.xxx.xxx.xxx:7003", "xxx.xxx.xxx.xxx:7012", "xxx.xxx.xxx.xxx:7009", "xxx.xxx.xxx.xxx:7019", "xxx.xxx.xxx.xxx:8001", "xxx.xxx.xxx.xxx:7023", "xxx.xxx.xxx.xxx:111", "xxx.xxx.xxx.xxx:7001", "xxx.xxx.xxx.xxx:8002", "xxx.xxx.xxx.xxx:19313", "xxx.xxx.xxx.xxx:15772", "xxx.xxx.xxx.xxx:19777", "xxx.xxx.xxx.xxx:15778", "xxx.xxx.xxx.xxx:15776"}
for _, ip := range ips {
wg.Add(1)
//time.Sleep(3 * time.Second)
go func(addr string) {
fmt.Printf("start to probe port %s\n", addr)
GetBaseServerStatus(strings.Split(addr, ":")[0], strings.Split(addr, ":")[1])
wg.Done()
}(ip)
}
wg.Wait()
fmt.Println("scan end")
time.Sleep(20 * time.Second)
}
however, run this code ,it consume 26GB memory
I use pprof to diagnose, see below
Showing top 10 nodes out of 15
flat flat% sum% cum cum%
26.29GB 96.86% 96.86% 26.29GB 96.86% go.mongodb.org/mongo-driver/x/network/connection.(*connection).ReadWireMessage

Reading TCP packets via raw sockets in GO

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

golang tcp socket does not send message after write immediately

My GO version is 1.1.1
the sever recieved messages after connection close, but NoDelay was setted.
Is there something wrong
addr, _ := net.ResolveTCPAddr("tcp", "localhost:5432")
conn, err := net.DialTCP("tcp", nil, addr)
defer conn.Close()
if err != nil {
fmt.Println("connect fail")
return
}
err = conn.SetNoDelay(true)
if err != nil {
fmt.Println(err.Error())
}
for {
var message string
_, err := fmt.Scanln(&message)
if err != nil && err.Error() != "unexpected newline" {
fmt.Println("input finished", err)
break
}
if message == "" {
fmt.Println("no input, end")
break
}
// message = fmt.Sprintf("%s\n",message)
//fmt.Fprintf(conn, message) // send immediately but following message won't send any more
conn.Write([]byte(message)) // won't send until connection close
}
There doesn't seem to be anything vitally wrong with your code so I'm guessing the error is on the server end.
If you create a local TCP server on port 5432 you can test this.
Try running the below server code and then test your client code against it. It just echos all received data to stdout.
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
l, err := net.Listen("tcp", "localhost:5432")
if err != nil {
log.Fatal(err)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
go func(c net.Conn) {
defer c.Close()
io.Copy(os.Stdout, c)
}(conn)
}
}
You should see each line sent to the client printed (without the newline) as soon as you hit enter.
the problem is on the server end.
func handleConnection(conn net.Conn) {
// I didn't put it in for loop
message, err := bufio.NewReader(conn).ReadString('\n')
}