You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言Raw Socket UDP编程问题:自定义IP/UDP头无接收反馈

Custom UDP Packets with TTL/IP Header Control in Go

Hey there! Let's tackle your problem of sending UDP packets with custom IP/UDP header fields (like TTL) in Go, and figure out why your current code isn't working.


a) Common Issues in Custom Packet Code

If your receiver isn't getting messages, here are the most likely mistakes to check:

  • Missing Privileges
    Raw sockets (required for custom IP headers) need root/administrator privileges. Make sure you're running your code with sudo (Linux/macOS) or as an admin (Windows).

  • Incorrect Socket Type
    Regular net.UDPConn doesn't let you modify IP headers. You need to use either a raw socket (syscall.SOCK_RAW with IPPROTO_UDP) or the golang.org/x/net/ipv4 package (a safer, higher-level wrapper).

  • Invalid Checksum Calculation
    When manually constructing IP/UDP headers, you have to calculate checksums correctly:

    • For IP headers: Compute the 16-bit one's complement sum of the entire IP header (with the checksum field set to 0 first)
    • For UDP headers: Include the "pseudo-header" (IP source/dest, protocol, UDP length) in the checksum calculation
  • Wrong Byte Order (Endianness)
    Network protocols use big-endian (network byte order) for fields like ports, IP lengths, and checksums. In Go, use syscall.Htons() for 16-bit values (like ports) to convert from host to network order.

  • Firewall/Routing Blocking Packets
    Use a tool like tcpdump (sudo tcpdump -i any udp port YOUR_PORT) to verify if packets are actually leaving your machine. If they are, check the receiver's firewall or routing rules.


Method 1: Use golang.org/x/net/ipv4 (Simplest for Common Fields)

This package wraps raw socket logic and lets you modify common IP header fields (TTL, TOS, DSCP) without manually building entire headers. It handles checksums and byte order automatically.

Here's a working example:

package main

import (
	"log"
	"net"
	"golang.org/x/net/ipv4"
)

func main() {
	// Establish a basic UDP connection to your target
	targetAddr := &net.UDPAddr{
		IP:   net.ParseIP("192.168.1.100"), // Replace with your receiver's IP
		Port: 8080,                        // Replace with your receiver's port
	}
	conn, err := net.DialUDP("udp", nil, targetAddr)
	if err != nil {
		log.Fatalf("Failed to dial UDP: %v", err)
	}
	defer conn.Close()

	// Create an ipv4.PacketConn to access IP header controls
	packetConn := ipv4.NewPacketConn(conn)

	// Set custom TTL value (replace 42 with your desired TTL)
	if err := packetConn.SetTTL(42); err != nil {
		log.Fatalf("Failed to set TTL: %v", err)
	}

	// Optional: Set other IP fields like Type of Service (TOS)
	if err := packetConn.SetTOS(0x10); err != nil {
		log.Fatalf("Failed to set TOS: %v", err)
	}

	// Send your message - kernel handles IP/UDP headers with your custom fields
	message := []byte("Hello with custom TTL!")
	bytesSent, err := packetConn.WriteTo(message, nil, targetAddr)
	if err != nil {
		log.Fatalf("Failed to send packet: %v", err)
	}
	log.Printf("Successfully sent %d bytes", bytesSent)
}

Method 2: Manual Raw Socket (For Full Header Control)

If you need to modify every single field in the IP/UDP header (e.g., IP ID, UDP checksum), use raw sockets. This requires more care but gives full control.

Here's a simplified snippet focused on header construction:

package main

import (
	"log"
	"net"
	"syscall"
)

// calculateChecksum computes the 16-bit one's complement checksum for IP/UDP headers
func calculateChecksum(data []byte) uint16 {
	sum := uint32(0)
	for i := 0; i < len(data); i += 2 {
		sum += uint32(data[i])<<8 | uint32(data[i+1])
	}
	// Fold 32-bit sum to 16 bits
	for sum>>16 != 0 {
		sum = (sum & 0xffff) + (sum >> 16)
	}
	return ^uint16(sum)
}

func main() {
	// Create raw socket
	fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_UDP)
	if err != nil {
		log.Fatalf("Failed to create raw socket: %v (need root?)", err)
	}
	defer syscall.Close(fd)

	// Tell kernel we'll provide the IP header
	err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
	if err != nil {
		log.Fatalf("Failed to set IP_HDRINCL: %v", err)
	}

	// Define target and source addresses
	srcIP := net.ParseIP("192.168.1.5").To4()
	dstIP := net.ParseIP("192.168.1.100").To4()
	srcPort := 12345
	dstPort := 8080
	message := []byte("Manual raw packet!")

	// Build IP header (20 bytes)
	ipHeader := make([]byte, 20)
	ipHeader[0] = 0x45 // Version (4) + IHL (5*4=20 bytes)
	ipHeader[1] = 0x00 // TOS
	totalLen := 20 + 8 + len(message) // IP header + UDP header + data
	ipHeader[2] = byte(totalLen >> 8) // Total length (big-endian)
	ipHeader[3] = byte(totalLen)
	ipHeader[4] = 0x00 // Identification (can customize)
	ipHeader[5] = 0x00
	ipHeader[6] = 0x00 // Flags + Fragment offset
	ipHeader[7] = 0x00
	ipHeader[8] = 42 // Custom TTL
	ipHeader[9] = syscall.IPPROTO_UDP // Protocol (UDP)
	ipHeader[10] = 0x00 // IP checksum (to be calculated)
	ipHeader[11] = 0x00
	copy(ipHeader[12:16], srcIP) // Source IP
	copy(ipHeader[16:20], dstIP) // Destination IP

	// Calculate and set IP checksum
	ipHeader[10], ipHeader[11] = func() (byte, byte) {
		cksum := calculateChecksum(ipHeader)
		return byte(cksum >> 8), byte(cksum)
	}()

	// Build UDP header (8 bytes)
	udpHeader := make([]byte, 8)
	udpHeader[0] = byte(syscall.Htons(uint16(srcPort)) >> 8) // Source port (big-endian)
	udpHeader[1] = byte(syscall.Htons(uint16(srcPort)))
	udpHeader[2] = byte(syscall.Htons(uint16(dstPort)) >> 8) // Destination port
	udpHeader[3] = byte(syscall.Htons(uint16(dstPort)))
	udpLen := 8 + len(message) // UDP header + data
	udpHeader[4] = byte(udpLen >> 8) // UDP length
	udpHeader[5] = byte(udpLen)
	udpHeader[6] = 0x00 // UDP checksum (can calculate or set to 0 for no checksum)
	udpHeader[7] = 0x00

	// Combine header and data
	fullPacket := append(ipHeader, udpHeader...)
	fullPacket = append(fullPacket, message...)

	// Prepare sockaddr for sending
	sockAddr := syscall.SockaddrInet4{
		Port: dstPort,
	}
	copy(sockAddr.Addr[:], dstIP)

	// Send the packet
	err = syscall.Sendto(fd, fullPacket, 0, &sockAddr)
	if err != nil {
		log.Fatalf("Failed to send packet: %v", err)
	}
	log.Println("Raw packet sent successfully")
}

Final Tips

  • Always test with tcpdump to confirm packets are being sent with your custom fields.
  • Prefer the x/net/ipv4 method unless you absolutely need full header control—it's less error-prone.
  • Remember to run raw socket code with root privileges.

内容的提问来源于stack exchange,提问作者Brash Man

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:42:21