太难了

This commit is contained in:
impact-eintr
2021-09-02 21:48:59 +08:00
parent 347bc5217b
commit 293b15a309
9 changed files with 196 additions and 9 deletions

BIN
img/链路层数据帧.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

38
tcpip/header/eth.go Normal file
View File

@@ -0,0 +1,38 @@
package header
import "github.com/impact-eintr/netstack/tcpip"
// 以太网帧头部信息的偏移量
const (
dstMAC = 0
srcMAC = 6
ethType = 12
)
// 表示链路层以太网帧的头部
type EthernetFields struct {
// 源地址
SrcAddr tcpip.LinkAddress
// 目标地址
DstAddr tcpip.LinkAddress
// 协议类型
Type tcpip.NetworkProtocolNumber
}
// 以太网数据包的封装
type Ethernet []byte
const (
// 以太网帧最小的长度
EthernetMinimumSize = 14
// 以太网帧的长度
EthernetAddressSize = 6
)
// 从帧头部获取源地址
// 从帧头部获取目的地址
// 从帧头部获取协议类型
// Encode根据传入的帧头部信息编码成Ethernet二进制形式

View File

@@ -501,3 +501,13 @@ ping 192.168.1.1
``` ```
这时候你 ping 192.168.1.0/24 网段的任何一个地址都是进入 tap0 网卡,这样我们就可以实验和处理 tap0 网上上的数据了。目前我们只看到了网卡有读取到数据,而且抓包显示我们现在接收到的数据都是 arp 请求,后面会实现对 arp 报文的处理,接下来我们开始处理网卡的数据并封装链路层,实现网卡的 io。 这时候你 ping 192.168.1.0/24 网段的任何一个地址都是进入 tap0 网卡,这样我们就可以实验和处理 tap0 网上上的数据了。目前我们只看到了网卡有读取到数据,而且抓包显示我们现在接收到的数据都是 arp 请求,后面会实现对 arp 报文的处理,接下来我们开始处理网卡的数据并封装链路层,实现网卡的 io。
### 链路层数据帧
数据在链路层传输都是一帧一帧传输的,就像发送邮件一样,将信放入信封中,接着把信封邮寄出去,这样可以把一段信息和另一段信息区分开来,下面先介绍数据帧格式。
![](../../img/链路层数据帧.png )\
- 目的 MAC 地址:目的设备的 MAC 物理地址。
- 源 MAC 地址:发送设备的 MAC 物理地址。
- 类型:表示后面所跟数据包的协议类型,例如 Type 为 0x8000 时为 IPv4 协议包Type 为 0x8060 时,后面为 ARP 协议包。
- 数据:表示该帧的数据内容,长度为 46 1500 字节,包含网络层、传输层和应用层的数据。

View File

@@ -0,0 +1,12 @@
package rawfile
import (
"syscall"
"unsafe"
)
func blockingPoll(fds *pollEvent, nfds int, timeout int64) (int, syscall.Errno) {
n, _, e := syscall.Syscall(syscall.SYS_POLL,
uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(timeout))
return int(n), e
}

View File

@@ -12,7 +12,7 @@ const maxErrno = 134
var translations [maxErrno]*tcpip.Error var translations [maxErrno]*tcpip.Error
func TranslationErrno(s syscall.Errno) *tcpip.Error { func TranslationErrno(s syscall.Errno) *tcpip.Error {
if err := translations[e]; err != nil { if err := translations[s]; err != nil {
return err return err
} }
return tcpip.ErrInvalidEndpointState return tcpip.ErrInvalidEndpointState

View File

@@ -23,7 +23,7 @@ func GetMTU(name string) (uint32, error) {
copy(ifreq.name[:], name) copy(ifreq.name[:], name)
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, _, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
uintptr(fd), syscall.SIOCGIFMTU, uintptr(&ifreq)) uintptr(fd), syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq)))
if errno != 0 { if errno != 0 {
return 0, errno return 0, errno
} }
@@ -74,3 +74,43 @@ type pollEvent struct {
events int16 events int16
revents int16 revents int16
} }
func BlockingRead(fd int, b []byte) (int, *tcpip.Error) {
for {
n, _, e := syscall.RawSyscall(syscall.SYS_READ,
uintptr(fd), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)))
if e == 0 {
return int(n), nil
}
event := pollEvent{
fd: int32(fd),
events: 1,
}
_, e = blockingPoll(&event, 1, -1)
if e != 0 && e != syscall.EINTR {
return 0, TranslationErrno(e)
}
}
}
func BlockingReadv(fd int, iovecs []syscall.Iovec) (int, *tcpip.Error) {
for {
n, _, e := syscall.RawSyscall(syscall.SYS_READV,
uintptr(fd), uintptr(unsafe.Pointer(&iovecs[0])), uintptr(len(iovecs)))
if e == 0 {
return int(n), nil
}
event := pollEvent{
fd: int32(fd),
events: 1,
}
_, e = blockingPoll(&event, 1, -1)
if e != 0 && e != syscall.EINTR {
return 0, TranslationErrno(e)
}
}
}

View File

@@ -1,16 +1,15 @@
package main package main
import ( import (
"log"
"github.com/impact-eintr/netstack/tcpip/link/rawfile"
"github.com/impact-eintr/netstack/tcpip/link/tuntap" "github.com/impact-eintr/netstack/tcpip/link/tuntap"
) )
func main() { func main() {
tapName := "tap0" tapName := "tap0"
c := &tuntap.Config{ c := &tuntap.Config{tapName, tuntap.TAP}
tapName,
tuntap.TAP,
}
fd, err := tuntap.NewNetDev(c) fd, err := tuntap.NewNetDev(c)
if err != nil { if err != nil {
panic(err) panic(err)
@@ -23,5 +22,11 @@ func main() {
buf := make([]byte, 1<<16) buf := make([]byte, 1<<16)
for { for {
rn, err := rawfile.BlockingRead(fd, buf)
if err != nil {
log.Println(err)
continue
}
log.Printf("read %d bytes\n", rn)
} }
} }

32
tcpip/link/tap2/main.go Normal file
View File

@@ -0,0 +1,32 @@
package main
import (
"log"
"github.com/impact-eintr/netstack/tcpip/link/rawfile"
"github.com/impact-eintr/netstack/tcpip/link/tuntap"
)
func main() {
tapName := "tap0"
c := &tuntap.Config{tapName, tuntap.TAP}
fd, err := tuntap.NewNetDev(c)
if err != nil {
panic(err)
}
// 启动tap网卡
_ = tuntap.SetLinkUp(tapName)
// 添加ip地址
_ = tuntap.AddIP(tapName, "192.168.1.0/24")
buf := make([]byte, 1<<16)
for {
rn, err := rawfile.BlockingRead(fd, buf)
if err != nil {
log.Println(err)
continue
}
log.Printf("read %d bytes\n", rn)
}
}

View File

@@ -1,5 +1,12 @@
package tcpip package tcpip
import (
"fmt"
"log"
"strconv"
"strings"
)
type Error struct { type Error struct {
msg string msg string
ignoreStats bool ignoreStats bool
@@ -23,8 +30,8 @@ var (
ErrBadLinkEndPoint = &Error{msg: "bad link layer endpoint"} ErrBadLinkEndPoint = &Error{msg: "bad link layer endpoint"}
ErrAlreadyBound = &Error{msg: "endpoint already bound", ignoreStats: true} ErrAlreadyBound = &Error{msg: "endpoint already bound", ignoreStats: true}
ErrInvalidEndpointState = &Error{msg: "endpoint is in invalid state"} ErrInvalidEndpointState = &Error{msg: "endpoint is in invalid state"}
ErrAlreadConnecting = &Error{msg: "endpoint is already connecting", ignoreStats: true} ErrAlreadyConnecting = &Error{msg: "endpoint is already connecting", ignoreStats: true}
ErrAlreadConnected = &Error{msg: "endpoint is already connected", ignoreStats: true} ErrAlreadyConnected = &Error{msg: "endpoint is already connected", ignoreStats: true}
ErrNoPortAvailable = &Error{msg: "no port are available"} ErrNoPortAvailable = &Error{msg: "no port are available"}
ErrPortInUse = &Error{msg: "port is in use"} ErrPortInUse = &Error{msg: "port is in use"}
ErrBadLocalAddress = &Error{msg: "bad local address"} ErrBadLocalAddress = &Error{msg: "bad local address"}
@@ -48,3 +55,46 @@ var (
ErrMessageTooLong = &Error{msg: "message too long"} ErrMessageTooLong = &Error{msg: "message too long"}
ErrNoBufferSpace = &Error{msg: "no buffer space available"} ErrNoBufferSpace = &Error{msg: "no buffer space available"}
) )
type LinkAddress string
func (a LinkAddress) String() string {
switch len(a) {
case 6:
// MAC地址的格式 6位 4bit的十六进制数
return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", a[0], a[1], a[2], a[3], a[4], a[5])
default:
return fmt.Sprintf("%x", []byte(a))
}
}
// aa:bb:cc:dd:ee:ff aa-bb-cc-dd-ee-ff
func ParseMACAddress(s string) (LinkAddress, error) {
parts := strings.FieldsFunc(s, func(c rune) bool {
return c == ':' || c == '-'
})
log.Println(parts)
if len(parts) != 6 {
return "", fmt.Errorf("inconsistent parts: %s", s)
}
addr := make([]byte, 0, len(parts))
for _, part := range parts {
u, err := strconv.ParseUint(part, 16, 8)
if err != nil {
return "", fmt.Errorf("invalid hex digits: %s", s)
}
addr = append(addr, byte(u))
}
return LinkAddress(addr), nil
}
type NetworkProtocolNumber uint32
type Address string
type ProtocolAddr struct {
Protocol NetworkProtocolNumber
Address Address
}