Hi all,
I’m looking for a way to send out UDP packets in a fast way. What’s special in my case is that i MUST be able to define the source port number (not only the destination port), and that I DO NOT expect any reply back via UDP (so no context is required). Also, the source port number is identical for all these packets, while the destination IP varies. I’m using several parallel goroutines (worker threads) to produce the UDP packets.
The only “standard” way I found to do this I found at http://ipengineer.net/2016/05/golang-net-package-udp-client-with-specific-source-port/, and it looks like this, shortened a bit to the essential (sorry if it’s not very elegant, but I’m new to Go…):
func sendUDP(dstIP string, dstPort int, localIP string, localPort uint, data []byte) {
RemoteEP := net.UDPAddr{IP: net.ParseIP(dstIP), Port: dstPort}
localAddrString := fmt.Sprintf("%s:%d", localIP, localPort)
LocalAddr, err := net.ResolveUDPAddr("udp", localAddrString)
if err != nil {...}
mutex.Lock()
defer mutex.Unlock()
conn, err := net.DialUDP("udp", LocalAddr, &RemoteEP)
if err != nil {...}
conn.Write(data)
conn.Close()
}
This works, but requires a UDP connection (without this I have no idea how I could define the UDP source port, it is defined by the OS), which is time consuming. Worse, because the source port is identical for all packets, I must serialize the sequence from opening until closing the connection by using a mutex; otherwise it would complain about not being able to bind to the port, as a UDP connection creates a listening server, and the source port number is the same for all packets. So, this approach becomes really slow.
One alternative I figured out is using the gopacket package to write directly to the network interface, then the code looks more or less like this (note that Ethernet, ip and udp headers as well as payloads are precalculated in a scanner object here):
func (s *scanner) sendUDPHW(dstIP string, idx int, ipPtr *layers.IPv4, udpPtr *layers.UDP, buf gopacket.SerializeBuffer) {
srcPort = s.srcPorts[idx]
pldPtr = &s.payloads[idx]
ipPtr.DstIP = net.ParseIP(dstIP)
udpPtr.SrcPort = layers.UDPPort(srcPort)
if err := gopacket.SerializeLayers(buf, s.opts, &s.eth, ipPtr, udpPtr, pldPtr); err != nil {...}
s.handle.WritePacketData(buf.Bytes())
}
This works very well, and is about 30 times faster than the standard approach. But of course it is highly hardware dependent, involves MAC addresses to construct the Ethernet layer, requires root privileges, and not all virtualizations allow this approach. Also, the user must supply at least the target MAC address (as the trick using a router object is only supported under linux). Even finding out the device names to send out plus the local MAC addresses are quite complicated, mainly under a Windows platform. I would prefer to be able to do this in the standard package… is there any way?
Thanks in advance,
Klaymen