Go中链路层套接字的实践
1. 介绍
接上次的博客,按照约定的划分,还有一层链路层socket。这一层就可以自定义链路层的协议头部(header)了,下面是目前主流的Ethernet 2(以太网)标准的头部:
相比IP和TCP的头部,以太网的头部要简单些,仅有目标MAC地址,源MAC地址,数据协议类型(比如常见的IP和ARP协议)。
但多了尾部的FCS(帧校验序列),用的是CRC校验法。如果校验错误,直接丢弃掉,不会送到上层的协议栈中,链路层只保证数据帧的正确性(丢掉错误的)。具体数据报的完整性由上层控制,比如TCP重传。
链路层最大长度是1518字节,除去18字节的头部和尾部,只剩1500字节,也就是MTU(最大传输单元)的由来,并约定最小传输长度64字节。
2. 服务端
用 ifonfig
查看本机的网络设备(网卡):
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)
通过Go提供的net拿到网络接口设备的详细信息,eth0是上面的网络设备名字:
ifi, err := net.InterfaceByName("eth0")
util.CheckError(err)
然后使用原始套接字绑定到该网络设备上:
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(wire.Htons(0x800)))
AF_PACKET是Linux 2.2加入的功能,可以在网络设备上接收发送数据包。其第二个参数 SOCK_RAW 表示带有链路层的头部,还有个可选值 SOCK_DGRAM 会移除掉头部。第三个则对应头部中协议类型(ehter type),比如只接收 IP 协议的数据,也可以接收所有的。可在Linux中if_ether文件查看相应的值。比如:
#define ETH_P_IP 0x0800 /* Internet Protocol packet
#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */
#define ETH_P_SNAP 0x0005 /* Internal only */
Htons函数是把网络字节序转成当前机器字节序。这里已经拿到链路层socket的连接句柄,下一步就可以监听该句柄的数据:
for {
buf := make([]byte, 1514)
n, _, _ := syscall.Recvfrom(fd, buf, 0)
header := wire.ParseHeader(buf[0:14])
fmt.Println(header)
}
这时候所有到这机器上的IP协议流量都能监听到,不管UDP,TCP,ICMP等上层协议。启动程序,尝试在另外台机器ping
下,得到:
root@4b56d41e5168:/ethernet# go run main.go
[2018-07-16T00:32:32.215Z] INFO 02:42:ac:11:00:02
DestinationAddress: 02:42:ac:11:00:02 SourceAddress: 02:42:ac:11:00:03 EtherType: ipv4
另外台机器:
root@3348477f42e8:/# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.202 ms
3. 协议头部
上面例子代码中,定义了1514的字节slice来接收一次以太网的数据,然后取出前14个字节来解析头部。协议尾部的4字节不需要处理,在发送数据的时候由网络设备并添加,接收的时候由设备校验并去除。在以前的有些计算机中,是需要自己添加或移除尾部的,后面可介绍下该校验算法。 ParseHeader解析头部也很简单,前6个字节是目标Mac地址,中间6字节是源Mac地址,后2字节是协议类型:
func ParseHeader(buf []byte) *Header {
header := new(Header)
var hd net.HardwareAddr
hd = buf[0:6]
header.DestinationAddress = hd
hd = buf[6:12]
header.SourceAddress = hd
header.EtherType = binary.BigEndian.Uint16(buf[12:14])
return header
}
ping使用的是ICMP协议,和TCP/UDP同级,所以根据接收到的数据继续解IP协议头部,ICMP协议头部。包含关系如图:
Go官方有相应的库可以解析:
ip4header, _ := ipv4.ParseHeader(buf[14:34])
fmt.Println("ipv4 header: ", ip4header)
icmpPayload := buf[34:]
msg, _ := icmp.ParseMessage(1, icmpPayload)
fmt.Println("icmp: ", msg)
IP头部20字节,ICMP头部8个字节,输出如下:
root@4b56d41e5168://ethernet# go run main.go
[2018-07-16T00:36:03.033Z] INFO 02:42:ac:11:00:02
DestinationAddress: 02:42:ac:11:00:02 SourceAddress: 02:42:ac:11:00:03 EtherType: ipv4
ipv4 header: ver=4 hdrlen=20 tos=0x0 totallen=84 id=0x97ab flags=0x2 fragoff=0x0 ttl=64 proto=1 cksum=0x4ad6 src=172.17.0.3 dst=172.17.0.2
icmp: &{echo 0 12964 0xc4200807e0}
4. 客户端
上面代码是服务端解析以太网协议头部,也可以自定义发送时头部:
建立socket句柄:
var ohter = net.HardwareAddr{0x02, 0x42, 0xac, 0x11, 0x00, 0x02}
var etherType uint16 = 52428
fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(wire.Htons(etherType)))
构建以太网头部,然后发送监听的机器上:
for {
payload := []byte("msg")
minPayload := len(payload)
if minPayload < 46 {
minPayload = 46
}
b := make([]byte, 14+minPayload)
header := &wire.Header{
DestinationAddress: broadcast,
SourceAddress: ifi.HardwareAddr,
EtherType: etherType,
}
copy(b[0:14], header.Marshal())
copy(b[14:14+len(payload)], payload)
var baddr [8]byte
copy(baddr[:], broadcast)
to := &syscall.SockaddrLinklayer{
Ifindex: ifi.Index,
Halen: 6,
Addr: baddr,
Protocol: wire.Htons(etherType),
}
err = syscall.Sendto(fd, b, 0, to)
util.CheckError(err)
time.Sleep(time.Second)
}
}
监听端输出:
root@4b56d41e5168:/ethernet# go run main.go
[2018-07-16T15:25:46.745Z] INFO 02:42:ac:11:00:02
DestinationAddress: 02:42:ac:11:00:02 SourceAddress: 02:42:ac:11:00:03 EtherType: unknow52428
DestinationAddress: 02:42:ac:11:00:02 SourceAddress: 02:42:ac:11:00:03 EtherType: unknow52428
5. 总结
基于此就可以抓取数据链路层的流量,然后对流量进行深入分析等。还有一种方式是基于packet_mmap的共享内存抓包方式,性能更好些。文中例子代码在examples,参考:
https://github.com/spotify/linux/blob/master/include/linux/if_ether.h
http://man7.org/linux/man-pages/man7/packet.7.html
Go中链路层套接字的实践的更多相关文章
- TCP/IP中链路层的附加数据(Trailer数据)和作用
1.TCP/IP中链路层的附加数据是什么 在用wireshark打开报文时,链路层显示的Trailer数据就是附加数据,如图 2.如何产生 1.例如以太网自动对小于64字节大小的报文进行填充(未实验) ...
- Python中利用原始套接字进行网络编程的示例
Python中利用原始套接字进行网络编程的示例 在实验中需要自己构造单独的HTTP数据报文,而使用SOCK_STREAM进行发送数据包,需要进行完整的TCP交互. 因此想使用原始套接字进行编程,直接构 ...
- JMeter中的HTTPS套接字错误
Apache JMeter对启用SSL的应用程序执行性能和/或负载测试时,SSL套接字错误可能是经常遇到的麻烦,严重阻碍了您的测试工作.本文重点介绍如何通过相应地配置和调优JMeter来克服这些与连接 ...
- TCP ------ TCP创建服务器中出现的套接字
在服务器端,socket()返回的套接字用于监听(listen)和接受(accept)客户端的连接请求.这个套接字不能用于与客户端之间发送和接收数据. accept()接受一个客户端的连接请求,并返回 ...
- Linux原始套接字实现分析---转
http://blog.chinaunix.net/uid-27074062-id-3388166.html 本文从IPV4协议栈原始套接字的分类入手,详细介绍了链路层和网络层原始套接字的特点及其内核 ...
- UNP——原始套接字
1.原始套接字的用处 使用原始套接字可以构造或读取网际层及其以上报文. 具体来说,可以构造 ICMP, IGMP 协议报文,通过开启 IP_HDRINCL 套接字选项,进而自定义 IPv4首部. 2. ...
- (转载)Linux 套接字编程中的 5 个隐患
在 4.2 BSD UNIX® 操作系统中首次引入,Sockets API 现在是任何操作系统的标准特性.事实上,很难找到一种不支持 Sockets API 的现代语言.该 API 相当简单,但新的开 ...
- Linux 套接字编程中的 5 个隐患
http://www.ibm.com/developerworks/cn/linux/l-sockpit/ 在 4.2 BSD UNIX® 操作系统中首次引入,Sockets API 现在是任何操作系 ...
- Linux 套接字编程中要注意的细节
隐患 1.忽略返回状态 第一个隐患很明显,但它是开发新手最容易犯的一个错误.如果您忽略函数的返回状态,当它们失败或部分成功的时候,您也许会迷失.反过来,这可能传播错误,使定位问题的源头变得困难. 捕获 ...
随机推荐
- sql server 阻塞查询
在生产环境下,有时公司客服反映网页半天打不到,除了在浏览器按F12的Network响应来排查,确定web服务器无故障后.就需要检查数据库是否有出现阻塞 当时数据库的生产环境中主表数据量超过2000w, ...
- 接口测试返回的json文件中字符串是乱序
问题描述 接口测试中post方式匹配返回信息时显示不匹配, 但是statuscode明明是200, 而且用postman /restclient等工具测出来也是没问题的. 根本原因 封装了这么个方法来 ...
- React从入门到放弃之前奏(4):Redux中间件
redux 提供了类似后端 Express 的中间件概念. 最适合扩展的是redux中的 store.dispatch 方法,中间件实际就是通过 override redux的store.dispat ...
- 如何修改Tomcat默认端口?
修改的原因: 关于8080端口:8080端口同80端口,是被用于WWW代理服务的,可以实现网页浏览,经常在访问某个网站或使用代理服务器的时候,会加上":8080"端口号.另外Apa ...
- 【最小生成树+贪心】BZOJ1821: [JSOI2010]Group 部落划分 Group
Description 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人们总是拉帮结派形成属于自己的部落,不同的部落之间则经常发生争斗.只是,这一切都成 ...
- 从MVC和三层架构说到SSH整合开发
相信很多人都认同JavaWeb开发是遵从MVC开发模式的,遵从三层架构进行开发的,是的,大家都这么认同.但是相信大家都会有过这样一个疑问,if(MVC三层模式==三层架构思想)out.println( ...
- 【爆料】-《阿伯泰大学毕业证书》Abertay一模一样原件
☞阿伯泰大学毕业证书[微/Q:865121257◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归&a ...
- openoffice excel word 转换pdf 支持本地调用和远程调用
OpenOffice.org 是一套跨平台的办公室软件套件,能在Windows.Linux.MacOS X (X11)和 Solaris 等操作系统上执行.它与各个主要的办公室软件套件兼容.OpenO ...
- ASP.Net Core Razor+AdminLTE 小试牛刀
AdminLTE 一个基于 bootstrap 的轻量级后台模板,这个前端界面个人感觉很清爽,对于一个大后端的我来说,可以减少较多的时间去承担前端的工作但又必须去独立去完成一个后台系统开发的任务,并且 ...
- 【STM32H7教程】第11章 STM32H7移植SEGGER的硬件异常分析
完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第11章 STM32H7移植SEGGER的硬 ...