mirror of
https://github.com/impact-eintr/netstack.git
synced 2025-10-11 15:50:05 +08:00
太难了
This commit is contained in:
BIN
img/链路层数据帧.png
Normal file
BIN
img/链路层数据帧.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
38
tcpip/header/eth.go
Normal file
38
tcpip/header/eth.go
Normal 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二进制形式
|
@@ -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。
|
||||||
|
|
||||||
|
### 链路层数据帧
|
||||||
|
数据在链路层传输都是一帧一帧传输的,就像发送邮件一样,将信放入信封中,接着把信封邮寄出去,这样可以把一段信息和另一段信息区分开来,下面先介绍数据帧格式。
|
||||||
|
|
||||||
|
\
|
||||||
|
|
||||||
|
- 目的 MAC 地址:目的设备的 MAC 物理地址。
|
||||||
|
- 源 MAC 地址:发送设备的 MAC 物理地址。
|
||||||
|
- 类型:表示后面所跟数据包的协议类型,例如 Type 为 0x8000 时为 IPv4 协议包,Type 为 0x8060 时,后面为 ARP 协议包。
|
||||||
|
- 数据:表示该帧的数据内容,长度为 46 ~ 1500 字节,包含网络层、传输层和应用层的数据。
|
||||||
|
12
tcpip/link/rawfile/blockingpoll_unsafe.go
Normal file
12
tcpip/link/rawfile/blockingpoll_unsafe.go
Normal 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
|
||||||
|
}
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -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
32
tcpip/link/tap2/main.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user