Skip to content

Instantly share code, notes, and snippets.

@roopeshsn
Created December 2, 2024 09:32
Show Gist options
  • Save roopeshsn/541d455f2adcb65170d0c4b0c377b32f to your computer and use it in GitHub Desktop.
Save roopeshsn/541d455f2adcb65170d0c4b0c377b32f to your computer and use it in GitHub Desktop.
Crafting an UDP Packet in Go
package main
import (
"context"
"fmt"
"log"
"net"
"syscall"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
type PacketConfig struct {
SrcMAC net.HardwareAddr
DstMAC net.HardwareAddr
SrcIP net.IP
DstIP net.IP
SrcPort layers.UDPPort
DstPort layers.UDPPort
PayloadSize int
}
func send(ctx context.Context, s *PacketConfig) error {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW)
if err != nil {
log.Fatalf("Failed to create raw socket: %v", err)
}
defer syscall.Close(fd)
// To manually include the IP header enable IP_HDRINCL
if err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1); err != nil {
log.Fatalf("Failed to set IP_HDRINCL: %v", err)
}
ipv4DstIP := s.DstIP.To4()
if ipv4DstIP == nil {
log.Fatalf("Destination IP address is not an IPv4 address: %s", s.DstIP)
}
dstAddr := &syscall.SockaddrInet4{
Port: int(s.DstPort),
Addr: [4]byte{ipv4DstIP[0], ipv4DstIP[1], ipv4DstIP[2], ipv4DstIP[3]},
}
packet, err := buildPacket(s)
fmt.Println(packet)
if err != nil {
return fmt.Errorf("failed to build packet: %w", err)
}
fmt.Println("Packet is created!")
select {
case <-ctx.Done():
return nil
default:
if err := syscall.Sendto(fd, packet, 0, dstAddr); err != nil {
return fmt.Errorf("failed to send packet: %v", err)
}
fmt.Println("Packet sent!")
}
return nil
}
func buildPacket(c *PacketConfig) ([]byte, error) {
buf := gopacket.NewSerializeBuffer()
var layersToSerialize []gopacket.SerializableLayer
ethLayer := &layers.Ethernet{
SrcMAC: c.SrcMAC,
DstMAC: c.DstMAC,
EthernetType: layers.EthernetTypeIPv4,
}
layersToSerialize = append(layersToSerialize, ethLayer)
ipLayer := &layers.IPv4{
Version: 4,
TTL: 64,
SrcIP: c.SrcIP,
DstIP: c.DstIP,
Protocol: layers.IPProtocolUDP,
}
layersToSerialize = append(layersToSerialize, ipLayer)
udpLayer := &layers.UDP{
SrcPort: c.SrcPort,
DstPort: c.DstPort,
}
udpLayer.SetNetworkLayerForChecksum(ipLayer)
layersToSerialize = append(layersToSerialize, udpLayer)
payload := make([]byte, c.PayloadSize)
layersToSerialize = append(layersToSerialize, gopacket.Payload(payload))
if err := gopacket.SerializeLayers(buf, gopacket.SerializeOptions{ComputeChecksums: true, FixLengths: true}, layersToSerialize...); err != nil {
return nil, fmt.Errorf("error serializing packet: %w", err)
}
return buf.Bytes(), nil
}
func main() {
fmt.Println("netctl v0.0.1. Born 7:04 pm Friday, 29 November 2024 Indian Standard Time (IST)")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
srcMAC, err := net.ParseMAC("de:ad:be:ef:ca:fe")
if err != nil {
fmt.Println("Error parsing MAC address:", err)
return
}
dstMAC, err := net.ParseMAC("de:ad:be:ef:ca:ff")
if err != nil {
fmt.Println("Error parsing MAC address:", err)
return
}
// now create a packet configuration
config := PacketConfig{
SrcMAC: srcMAC,
DstMAC: dstMAC,
SrcIP: net.ParseIP("191.191.191.191"),
DstIP: net.ParseIP("192.192.192.192"),
SrcPort: 12345,
DstPort: 12346,
PayloadSize: 100,
}
err = send(ctx, &config)
if err != nil {
fmt.Println(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment