如何在Golang中实现Raw Socket原始套接字编程 Go语言构造IP头

浅辰姑娘_8779

浅辰姑娘_8779

2026-03-19

546人浏览

原创

Go标准库禁用原始套接字是出于安全设计,需通过syscall在Linux/macOS/Windows上手动构造IP头并设IP_HDRINCL,注意权限、校验和、源IP匹配及容器环境限制。

如何在golang中实现raw socket原始套接字编程 go语言构造ip头

Go 里没有 socket(AF_INET, SOCK_RAW, ...) 的标准库支持

Go 标准库的 net 包刻意屏蔽了原始套接字能力,net.ListenIP 和 net.DialIP 只能操作三层以上(即 IP 层之上的 TCP/UDP),无法构造自定义 IP 头或发送无协议校验和的数据包。这不是遗漏,是设计选择:防止普通应用误用 raw socket 引发安全或网络问题。

真正需要构造 IP 头(比如写 ICMP 探测、自定义隧道、协议栈实验),必须绕过标准库:

  • Linux 下用 syscall.Socket + syscall.SetsockoptInt 开启 IP_HDRINCL,再手动填充 IP header + payload
  • macOS 不支持 IP_HDRINCL,只能用 BPF(需 root + 特殊权限)
  • Windows 需要 WSASocket + IP_HDRINCL,且仅限管理员权限

Linux 上用 syscall 构造带自定义 IP 头的包

核心是三步:创建 raw socket → 设置 IP_HDRINCL → 自己填满整个 IP 数据报(含 IP 头 + 上层数据)。Go 的 syscall 包可完成,但要注意字节序、校验和、字段对齐。

常见错误现象:bind: permission denied(没加 sudo)、invalid argument(IP 头长度不对或校验和为 0)、发出去的包被内核静默丢弃(IP_HDRINCL 没设成功)。

  • IPPROTO_RAW 是创建 socket 时的 protocol 参数,不是 IPPROTO_ICMP —— 后者会让内核自动补 IP 头
  • IP 头中 TotalLength 字段必须是整个 IP 包长度(header + payload),且用 network byte order(大端)
  • Checksum 字段在启用 IP_HDRINCL 后必须由用户计算并填入,不能为 0;可用 binary.BigEndian.PutUint16(buf[10:12], checksum) 写入
  • 目标地址填在 IP 头的 DstIP 字段,sendto 的 sockaddr_in 地址结构里的 sin_addr 可以填 0(因为头里已有)
// 简化示意:构造一个最小 IP 头(20 字节)+ 8 字节 payload
hdr := make([]byte, 20)
hdr[0] = 0x45          // IPv4 + IHL=5
hdr[1] = 0x00          // DSCP/ECN
binary.BigEndian.PutUint16(hdr[2:4], uint16(len(hdr)+8)) // TotalLength
binary.BigEndian.PutUint16(hdr[4:6], 0)                   // ID
binary.BigEndian.PutUint16(hdr[6:8], 0x4000)              // Flags + FragOffset
hdr[8] = 64            // TTL
hdr[9] = 0x01          // Protocol (ICMP)
hdr[10] = 0x00         // Checksum placeholder(稍后填)
hdr[12] = 192; hdr[13] = 168; hdr[14] = 1; hdr[15] = 1      // SrcIP
hdr[16] = 192; hdr[17] = 168; hdr[18] = 1; hdr[19] = 2      // DstIP

别直接用 net.Interface.Addrs() 获取本地 IP 来填源地址

Raw socket 发包时,源 IP 必须和实际出口网卡匹配,否则路由失败或触发反向路径过滤(RPF)。net.Interface.Addrs() 返回的是接口配置的 IP,但不保证该接口是默认路由出口。

Golang Uber Dig
Golang Uber Dig

使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等

下载

更可靠的做法是查路由表,或者用 net.Dial("udp", "8.8.8.8:53") 连一个外部地址,再从 LocalAddr() 提取实际使用的源 IP —— 这模拟了真实出向路径。

  • 如果填错源 IP(比如填了 loopback 或未启用的 IP),包可能发不出,tcpdump 里看不到,也无错误返回
  • 某些云环境(如 AWS EC2)禁用非主网卡 IP 的 raw socket 发包,即使权限够也会静默失败
  • IPv6 下类似,但要用 IPV6_HDRINCL,且结构体布局和校验和算法不同

第三方库不是银弹,gopacket 也要 root 权限

有人会立刻想到 github.com/google/gopacket,它封装了底层细节,支持跨平台 raw socket 抽象。但注意:gopacket 的 afpacket(Linux)或 pcap(macOS/Windows)后端仍需 root 或 administrator 权限,且默认只提供抓包能力;发包仍依赖 pcap.Inject 或 afpacket.WriteTo,本质还是调用系统 raw socket。

它省去的是手动填 IP 头、算校验和、处理字节序,但不会绕过权限检查或内核限制。如果你只是想发个 ping,用 golang.org/x/net/icmp 更轻量;真要改 IP 头字段(比如伪造 TTL、设置特定 TOS),还是得回到 syscall 层。

容易被忽略的一点:raw socket 在容器里默认被禁用。Docker/Kubernetes 需显式加 --cap-add=NET_RAW,且 PodSecurityPolicy 或 PodSecurityAdmission 可能拦截。不是代码问题,是运行时环境卡住。

golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!

相关文章

编程速学教程(入门课程)
编程速学教程(入门课程)

编程怎么学习?编程怎么入门?编程在哪学?编程怎么学才快?不用担心,这里为大家提供了编程速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

golang go语言

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

3489

3

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

2023.07.25

2408

9

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

2023.08.02

1340

5

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.09

1298

4

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

2023.09.05

1396

5

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

2023.09.20

2298

7

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

2023.09.20

3760

8

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

2023.09.22

17615

6

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

2023.09.22

549

3

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程