diff --git a/img/链路层数据帧.png b/img/链路层数据帧.png new file mode 100644 index 0000000..ac3e52d Binary files /dev/null and b/img/链路层数据帧.png differ diff --git a/tcpip/header/eth.go b/tcpip/header/eth.go new file mode 100644 index 0000000..b7f115e --- /dev/null +++ b/tcpip/header/eth.go @@ -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二进制形式 diff --git a/tcpip/link/README.md b/tcpip/link/README.md index c5375b9..59d3726 100644 --- a/tcpip/link/README.md +++ b/tcpip/link/README.md @@ -501,3 +501,13 @@ ping 192.168.1.1 ``` 这时候你 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 字节,包含网络层、传输层和应用层的数据。 diff --git a/tcpip/link/rawfile/blockingpoll_unsafe.go b/tcpip/link/rawfile/blockingpoll_unsafe.go new file mode 100644 index 0000000..b533a6e --- /dev/null +++ b/tcpip/link/rawfile/blockingpoll_unsafe.go @@ -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 +} diff --git a/tcpip/link/rawfile/errors.go b/tcpip/link/rawfile/errors.go index 44e7c55..4cbb34c 100644 --- a/tcpip/link/rawfile/errors.go +++ b/tcpip/link/rawfile/errors.go @@ -12,7 +12,7 @@ const maxErrno = 134 var translations [maxErrno]*tcpip.Error func TranslationErrno(s syscall.Errno) *tcpip.Error { - if err := translations[e]; err != nil { + if err := translations[s]; err != nil { return err } return tcpip.ErrInvalidEndpointState diff --git a/tcpip/link/rawfile/rawfile_unsafe.go b/tcpip/link/rawfile/rawfile_unsafe.go index d423a40..5385351 100644 --- a/tcpip/link/rawfile/rawfile_unsafe.go +++ b/tcpip/link/rawfile/rawfile_unsafe.go @@ -23,7 +23,7 @@ func GetMTU(name string) (uint32, error) { copy(ifreq.name[:], name) _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, - uintptr(fd), syscall.SIOCGIFMTU, uintptr(&ifreq)) + uintptr(fd), syscall.SIOCGIFMTU, uintptr(unsafe.Pointer(&ifreq))) if errno != 0 { return 0, errno } @@ -74,3 +74,43 @@ type pollEvent struct { events 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) + } + } +} diff --git a/tcpip/link/tap1/main.go b/tcpip/link/tap1/main.go index c934bed..c688ebe 100644 --- a/tcpip/link/tap1/main.go +++ b/tcpip/link/tap1/main.go @@ -1,16 +1,15 @@ 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, - } - + c := &tuntap.Config{tapName, tuntap.TAP} fd, err := tuntap.NewNetDev(c) if err != nil { panic(err) @@ -23,5 +22,11 @@ func main() { 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) } } diff --git a/tcpip/link/tap2/main.go b/tcpip/link/tap2/main.go new file mode 100644 index 0000000..ada0b70 --- /dev/null +++ b/tcpip/link/tap2/main.go @@ -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) + } +} diff --git a/tcpip/tcpip.go b/tcpip/tcpip.go index 8243a1d..90c74a6 100644 --- a/tcpip/tcpip.go +++ b/tcpip/tcpip.go @@ -1,5 +1,12 @@ package tcpip +import ( + "fmt" + "log" + "strconv" + "strings" +) + type Error struct { msg string ignoreStats bool @@ -23,8 +30,8 @@ var ( ErrBadLinkEndPoint = &Error{msg: "bad link layer endpoint"} ErrAlreadyBound = &Error{msg: "endpoint already bound", ignoreStats: true} ErrInvalidEndpointState = &Error{msg: "endpoint is in invalid state"} - ErrAlreadConnecting = &Error{msg: "endpoint is already connecting", ignoreStats: true} - ErrAlreadConnected = &Error{msg: "endpoint is already connected", ignoreStats: true} + ErrAlreadyConnecting = &Error{msg: "endpoint is already connecting", ignoreStats: true} + ErrAlreadyConnected = &Error{msg: "endpoint is already connected", ignoreStats: true} ErrNoPortAvailable = &Error{msg: "no port are available"} ErrPortInUse = &Error{msg: "port is in use"} ErrBadLocalAddress = &Error{msg: "bad local address"} @@ -48,3 +55,46 @@ var ( ErrMessageTooLong = &Error{msg: "message too long"} 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 +}