您现在的位置是:主页 > news > 青岛网站设计公司/杭州seo技术培训

青岛网站设计公司/杭州seo技术培训

admin2025/6/7 21:59:00news

简介青岛网站设计公司,杭州seo技术培训,怎样做网站的关键词,8免费网站建站51Reboot 运维开发Golang 课程1.18开课K8S 课程Python 自动化进阶课程Python 基础实战课程运维前端课程课程试听预约请扫码>>>1. 介绍接上次的博客,按照约定的划分,还有一层链路层 socket。这一层就可以自定义链路层的协议头部 (header)了&…

青岛网站设计公司,杭州seo技术培训,怎样做网站的关键词,8免费网站建站51Reboot 运维开发Golang 课程1.18开课K8S 课程Python 自动化进阶课程Python 基础实战课程运维前端课程课程试听预约请扫码>>>1. 介绍接上次的博客,按照约定的划分,还有一层链路层 socket。这一层就可以自定义链路层的协议头部 (header)了&…
51Reboot 运维开发

Golang 课程1.18开课

K8S 课程

Python 自动化进阶课程

Python 基础实战课程

运维前端课程

课程试听预约请扫码>>>

5dd48f04d6c14e114fa73dafbabdaaac.png

1. 介绍

接上次的博客,按照约定的划分,还有一层链路层 socket。这一层就可以自定义链路层的协议头部 (header)了,下面是目前主流的 Ethernet 2 (以太网)标准的头部:

0b25ca86f81ee7a93e522cc30560f108.png

相比 IP 和 TCP 的头部,以太网的头部要简单些,仅有目标 MAC 地址,源 MAC 地址,数据协议类型(比如常见的 IP 和 ARP 协议)。但多了尾部的 FCS (帧校验序列),用的是 CRC 校验法。如果校验错误,直接丢弃掉,不会送到上层的协议栈中,链路层只保证数据帧的正确性(丢掉错误的)。具体数据报的完整性由上层控制,比如  TCP 重传。


链路层最大长度是1518字节,除去18字节的头部和尾部,只剩1500字节,也就是MTU (最大传输单元)的由来,并约定最小传输长度64字节。

2. 服务端

用 ifonfig 查看本机的网络设备(网卡):

eth0: flags=4163  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:02DestinationAddress: 02:42:ac:11:00:02 SourceAddress: 02:42:ac:11:00:03 EtherType: ipv4

另外台机器:

root@3348477f42e8:/# ping 172.17.0.2PING 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 协议头部。包含关系如图:

427f6c3a773f5e8bed5365a024173a40.png

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:02DestinationAddress: 02:42:ac:11:00:02 SourceAddress: 02:42:ac:11:00:03 EtherType: ipv4ipv4 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.2icmp:  &{echo 0 12964 0xc4200807e0}

4. 客户端

上面代码是服务端解析以太网协议头部,也可以自定义发送时头部。
建立 socket 句柄:

var ohter = net.HardwareAddr{0x02, 0x42, 0xac, 0x11, 0x00, 0x02}var etherType uint16 = 52428fd, 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:02DestinationAddress: 02:42:ac:11:00:02 SourceAddress: 02:42:ac:11:00:03 EtherType: unknow52428DestinationAddress: 02:42:ac:11:00:02 SourceAddress: 02:42:ac:11:00:03 EtherType: unknow52428
5. 总结

基于此就可以抓取数据链路层的流量,然后对流量进行深入分析等。还有一种方式是基于 packet_mmap 的共享内存抓包方式,性能更好些。文中例子代码在 examples(https://github.com/mushroomsir/blog/tree/master/examples/002),参考:
https://github.com/spotify/linux/blob/master/include/linux/if_ether.h
http://man7.org/linux/man-pages/man7/packet.7.html

作者:蘑菇先生 

出处: http://mushroom.cnblogs.com/

跟着小编一起来学 Go

上期每日一练答案揭晓
package day1import (  "fmt"  "unicode/utf8")//First  创建一个基于 for 的简单的循环。使其循环 10 次,并且使用 fmt 包打印出计数器的值。func First() {  fmt.Println("first func start")  for i := 1; i < 11; i++ {    fmt.Println(i)  }  fmt.Println("first func end")}//Second  用 goto 改写 1 的循环。关键字 for 不可使用。func Second() {  fmt.Println("second func start")  i := 1HERE:  fmt.Println(i)  if i < 10 {    i++    goto HERE  }  fmt.Println("second func end")}//Third  再次改写这个循环,使其遍历一个 array,并将这个 array 打印到屏幕上。func Third() {  fmt.Println("third func start")  arr := [5]int{1, 2, 3, 4, 5}  for i, v := range arr {    fmt.Printf("第%d个元素为%d\n", i+1, v)  }  fmt.Println("third func end")}//FizzBuzz  编写一个程序,打印从 1 到 100 的数字。当是三的倍数就打印 “Fizz”代替数字,//当是5的倍数就打印 “Buzz” 。当数字同时是三和5的倍数时,打印 “FizzBuzz” 。func FizzBuzz(number int) {  fmt.Println("FizzBuzz func start")  for i := 1; i <= number; i++ {    switch true {    case i%3 == 0 && i%5 == 0:      fmt.Printf("%s ", "FizzBuzz")    case i%3 == 0:      fmt.Printf("%s ", "Fizz")    case i%5 == 0:      fmt.Printf("%s ", "Buzz")    default:      fmt.Printf("%d ", i)    }  }  fmt.Println()  fmt.Println("FizzBuzz func end")}//PrintA 建立一个 Go 程序打印下面的内容(到 100 个字符):A// AA// AAA// AAAA// AAAAA// AAAAAA// AAAAAAAfunc PrintA(number int) {  a := "A"  for i := 0; i < number; i++ {    fmt.Println(a)    a = a + "A"  }}
每日一练

1、编写函数,返回其(两个)参数正确的(自然)数字顺序。

2、创建一个固定大小保存整数的栈。它无须超出限制的增长。定义 push 函数—— 将数据放入栈,和 pop 函数——从栈中取得内容。栈应当是后进先出(LIFO) 的。

    • 更进一步。编写一个 String 方法将栈转化为字符串形式的表达。可以这样的 方式打印整个栈:fmt.Printf("My stack %v\n", stack) 栈可以被输出成这样的形式:[0:m] [1:l] [2:k]

3、编写函数接受整数类型变参,并且每行打印一个数字。

 4、斐波那契数列以:1,1,2,3,5,8,13,... 开始。或者用数学形式表达:x 1 = 1;x 2 = 1;x n = x n−1 + x n−2 ∀n > 2。编写一个接受 int 值的函数,并给出这个值得到的斐波那契数列。

答案下期揭晓

让我知道你在看

260eb91c8b89ea76cb4618ecf3b1bb93.gif