diff --git a/.docker/go/Dockerfile b/.docker/go/Dockerfile index a0e4506..11e2b52 100644 --- a/.docker/go/Dockerfile +++ b/.docker/go/Dockerfile @@ -2,4 +2,4 @@ FROM golang:1.19 ENV GO111MODULE on -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y iproute2 iputils-ping dnsutils traceroute iperf3 lsof tcpdump wireshark vim +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y iproute2 iputils-ping dnsutils iperf3 vim tcpdump lsof diff --git a/README.md b/README.md index 9b00809..6e232ba 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,129 @@ -# 📣 echoman: simple echo server +# 🐶 echoman: Simple echo server using Rawsocket -## 🌱 Overview -- This is a simple tool where a client generates packets over TCP/IP and sends them to a server. -- The echo server echoes data by swapping src/dst L3 address and L4 port number. (However, some parameters such as TypeCode and CheckSum are recalculated.) +## 🌱 Overview +- これはTCP/IPの上でClientがパケットを生成してServerへ送信するだけの簡単なツールです +- Serverはsrc/dst L2,L3アドレス, L4ポート番号を入れ替えてデータをechoします(ただし、TypeCodeやCheckSumなどの一部パラメータは再計算する) +- Rawsocketを使用してL2レベル(`syscall.ETH_P_IP`)でパケットを操作 + - **LayersType: `LayerTypeEthernet`** -## ✏️ Documents -- [Information: Network design](./docs/01_information.md) -- [Application: An application that communicates over the overlay network](./docs/02_application.md) -- [Usage: How to start echoman](./docs/03_usage.md) -- [Appendix: About sockets](./docs/04_appendix.md) +## ⚡️ Generating socket descriptors +### Socket functions +ansible + +### Used function +| args | syscall | +| :--- | :---: | +| Domain | `AF_PACKET` | +| Type | `SOCK_RAW` | +| Protocols | `ETH_P_IP` | + +```go +### 送信ソケットの生成 +func etherSendSock(intfIndex *net.Interface) (int, error) { + fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_IP))) + if err != nil { + return -1, err + } + + addr := syscall.SockaddrLinklayer{ + Protocol: htons(syscall.ETH_P_ALL), + Ifindex: intfIndex.Index, + } + + if err := syscall.Bind(fd, &addr); err != nil { + return -1, err + } + + return fd, nil +} + +### 受信ソケットの生成 +func etherRecvSock(intfIndex *net.Interface) (int, error) { + fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_IP))) + if err != nil { + return -1, err + } + + addr := syscall.SockaddrLinklayer{ + Protocol: htons(syscall.ETH_P_ALL), + Ifindex: intfIndex.Index, + } + if err := syscall.Bind(fd, &addr); err != nil { + return -1, err + } + + // Received in promiscuous mode + if err := syscall.SetLsfPromisc(intfIndex.Name, true); err != nil { + return -1, err + } + + return fd, nil +} + +### Ethernetパケット(L2~)の送信 +func SendEtherPacket(fd int, b []byte) error { + if _, err := syscall.Write(fd, b); err != nil { + return err + } + + return nil +} +``` + +## 🤝 Requirement +- **⚠️WARNING**:https://docs.docker.com/desktop/previous-versions/3.x-mac/#new-9 + +> First version of docker compose (as an alternative to the existing docker-compose). Supports some basic commands but not the complete functionality of docker-compose yet. + +| Languages / Frameworks | Version | +| :--- | ---: | +| Golang | 1.19.1 | +| Docker Desktop | 4.12.0 | +| docker | 20.10.17 | +| docker-compose | 1.29.2 | + +## 🚀 Usage +```sh +### envをコピー +$ cp server/.env{.sample,} +$ cp client/.env{.sample,} + +### .envの中身を環境に合わせて書き換える +※注意: Docker networkではMACアドレスはランダムに生成されます + +### docker-composeを起動 +$ make up + +### 実行 +--- +### 1枚目のTerminal: Echoman server を起動 +$ make exec/server +# make run + +### 2枚目のTerminal: Echoman client を起動 +$ make exec/client +# make run +--- + +### Echoman clientはデフォルトでUDPを喋ります +-> .env を編集することで生成するパケットタイプを切り替える +PACKET_TYPE=[パケットタイプ] +``` +| PacketType | Env Value | +| :--- | :---: | +| ICMPv4 | `ICMPV4` | +| UDPv4 | `UDPV4` | + +## 📖 Default Information + +| Device | Information | +| :--- | ---: | +| Echoman Server IPv4 address | `10.0.3.95` | +| Echoman Server Port number | `30005` | +| Echoman Client IPv4 address | `10.0.3.96` | +| Echoman Client Port number | `30006` | ## 📚 References -- [RFC 792 - Internet Control Message Protocol](https://www.rfc-editor.org/rfc/rfc792) -- [RFC 768 - User Datagram Protocol](https://www.rfc-editor.org/rfc/rfc768) -- [Universal TUN/TAP device driver](https://docs.kernel.org/networking/tuntap.html) -- [Checksum calculation](https://o21o21.hatenablog.jp/entry/2019/01/31/120436) +- [RFC 792](https://www.rfc-editor.org/rfc/rfc792) +- [RFC 768](https://www.rfc-editor.org/rfc/rfc768) +- [Checksum計算](https://o21o21.hatenablog.jp/entry/2019/01/31/120436) diff --git a/client/.env.sample b/client/.env.sample index de5370f..569734b 100644 --- a/client/.env.sample +++ b/client/.env.sample @@ -1,15 +1,10 @@ -DEBUG_MODE=true -VIRTUAL_IP_TYPE=4 - LOCAL_INTERFACE=eth0 +LOCAL_UDP_PORT=30006 PEER_IPV4_ADDRESS=10.0.3.95 -PEER_IPV6_ADDRESS=fde4:db8::0395 -PEER_UDP_PORT=30000 +PEER_MAC_ADDRESS=02:42:0a:00:03:5f +PEER_UDP_PORT=30005 -TUN_INTERFACE_NAME=echoman_tun +PACKET_TYPE=UDPV4 -ECHOMAN_SERVER_IPV4_TUN=198.18.9.10 -ECHOMAN_SERVER_IPV6_TUN=2001:db8:c0ff:ee00:0910:0910:0910:0910 - -CHROUS_PORT=30910 +DEBUG_MODE=true diff --git a/client/Makefile b/client/Makefile index 9dfd672..f0bb7ed 100644 --- a/client/Makefile +++ b/client/Makefile @@ -8,8 +8,7 @@ GODOC=$(GOCMD)doc GOLANGCI=golangci-lint DUMPCMD=tcpdump -INTERFACE_REAL=eth0 -INTERFACE_VIRTUAL=echoman_tun +INTERFACE=eth0 @@ -43,17 +42,11 @@ coverage: ## coverage go test -coverprofile=cover.out ./... go tool cover -html=cover.out -o cover.html -dump-real: ## dump packet - $(DUMPCMD) -i ${INTERFACE_REAL} +dump: ## dump packet + $(DUMPCMD) -i ${INTERFACE} -dump-virtual: ## dump packet - $(DUMPCMD) -i ${INTERFACE_VIRTUAL} - -dump-real/out: ## dump packet and output to file - $(DUMPCMD) -i ${INTERFACE_REAL} -w ./debug/dumpfile-client-real.pcapng - -dump-virtual/out: ## dump packet and output to file - $(DUMPCMD) -i ${INTERFACE_VIRTUAL} -w ./debug/dumpfile-client-virtual.pcapng +dump/out: ## dump packet and output to file + $(DUMPCMD) -i ${INTERFACE} -w ./debug/dumpfile-client.pcapng listen: ## check listen port lsof -i -P diff --git a/client/chorus/generate_request.go b/client/chorus/generate_request.go deleted file mode 100644 index 70a2187..0000000 --- a/client/chorus/generate_request.go +++ /dev/null @@ -1,130 +0,0 @@ -package chorus - -import ( - "net" - - "github.com/GotoRen/echoman/client/internal/logger" - "github.com/google/gopacket" - golayers "github.com/google/gopacket/layers" -) - -// GenerateUDPRequestPacket generates udp request packet. -func GenerateUDPRequestPacket(srcVIPv4, dstVIPv4 net.IP, chorusPort golayers.UDPPort) ([]byte, error) { - data := []byte("Ping") - - ip := golayers.IPv4{ - Version: 4, - Protocol: golayers.IPProtocolUDP, - SrcIP: srcVIPv4, - DstIP: dstVIPv4, - } - - udp := golayers.UDP{ - SrcPort: chorusPort, - DstPort: chorusPort, - } - - // calculating checksum - if err := udp.SetNetworkLayerForChecksum(&ip); err != nil { - logger.LogErr("udp set error", "error", err) - return nil, err - } - - options := gopacket.SerializeOptions{ - ComputeChecksums: true, - FixLengths: true, - } - - buffer := gopacket.NewSerializeBuffer() - - if err := gopacket.SerializeLayers(buffer, options, - &ip, - &udp, - gopacket.Payload(data), - ); err != nil { - logger.LogErr("Serialize error", "error", err) - return nil, err - } - - // _Test - // outgoing := []byte{ - // 0x45, 0x00, 0x00, 0x20, - // 0x0d, 0x73, 0x00, 0x00, - // 0x00, 0x11, 0x3c, 0xf2, - // 0xc6, 0x12, 0xdb, 0x39, - // 0xc6, 0x12, 0x09, 0x0a, - // 0x75, 0x30, 0x75, 0x30, - // 0x00, 0x0c, 0xe6, 0x3b, - // 0x50, 0x69, 0x6e, 0x67, - // } - - // return outgoing, nil - return buffer.Bytes(), nil -} - -// func (device *Device) NewICMPv4Packet() []byte { -// ether := golayers.Ethernet{ -// DstMAC: device.Peer.PeerMAC, -// SrcMAC: device.LocalMAC, -// EthernetType: golayers.EthernetTypeIPv4, -// } - -// ip := golayers.IPv4{ -// Version: 4, -// TOS: 0, -// Length: 0, -// Id: 0, -// FragOffset: 0, -// TTL: 255, -// Protocol: golayers.IPProtocolICMPv4, -// Checksum: 0, -// SrcIP: device.LocalIPv4, -// DstIP: device.Peer.PeerIPv4, -// } - -// icmpv4 := golayers.ICMPv4{ -// TypeCode: golayers.CreateICMPv4TypeCode(8, 0), -// Checksum: 0, -// Id: 10, -// Seq: 1, -// } - -// // ICMPv4 Max payload length: 48 byte -// req_data := []byte{ -// 34, 43, 67, 99, -// 0, 0, 0, 0, -// 49, 129, 5, 0, -// 0, 0, 0, 0, -// 16, 17, 18, 19, -// 21, 22, 23, 24, -// 25, 26, 27, 28, -// 29, 30, 31, 32, -// 33, 34, 35, 36, -// 37, 38, 39, 40, -// 41, 42, 43, 44, -// 45, 46, 47, 48, -// 49, 50, 51, 52, -// 53, 54, 55, 20, -// } - -// options := gopacket.SerializeOptions{ -// ComputeChecksums: true, -// FixLengths: true, -// } - -// buffer := gopacket.NewSerializeBuffer() - -// if err := gopacket.SerializeLayers(buffer, options, -// ðer, -// &ip, -// &icmpv4, -// gopacket.Payload(req_data), -// ); err != nil { -// logger.LogErr("Serialize error", "error", err) -// return nil -// } - -// outgoingPacket := buffer.Bytes() - -// return outgoingPacket -// } diff --git a/client/cmd/main.go b/client/cmd/main.go index dfd65b8..4074044 100644 --- a/client/cmd/main.go +++ b/client/cmd/main.go @@ -1,9 +1,65 @@ package main import ( - "github.com/GotoRen/echoman/client/exec" + "fmt" + "os" + "syscall" + "time" + + "github.com/GotoRen/echoman/client/internal" + "github.com/GotoRen/echoman/client/internal/logger" + "github.com/joho/godotenv" ) +func init() { + err := godotenv.Load() + if err != nil { + logger.LogErr("Error loading .env file", "error", err) + } +} + func main() { - exec.Run() + logger.InitZap() + t := time.NewTicker(time.Second * 1) + + paket_type := os.Getenv("PACKET_TYPE") + + device := internal.GetDeviceInfo(os.Getenv("LOCAL_INTERFACE")) + fmt.Println("[INFO] Local Interface Information:", device.IfIndex) + fmt.Println("[INFO] Local IPv4 Address:", device.LocalIPv4) + fmt.Println("[INFO] Local Hardware Address:", device.LocalMAC) + fmt.Println("[INFO] Peer IPv4 Address:", device.Peer.PeerIPv4) + fmt.Println("[INFO] Peer Hardware Address:", device.Peer.PeerMAC) + + device.CreateDescriptor() + defer syscall.Close(device.Sd4soc) + defer syscall.Close(device.Rv4soc) + + device.ListenClient() + + for { + <-t.C + + switch paket_type { + case "ICMPV4": + device.GenerateICMPv4Packet(device.Sd4soc) + case "UDPV4": + device.GenerateUDPPacket(device.Sd4soc) + default: + logger.LogErr("You chose a mode that doesn't exist", "error", paket_type) + os.Exit(1) + } + + buf := make([]byte, 1500) + size, _, err := syscall.Recvfrom(device.Rv4soc, buf, 0) + if err != nil { + fmt.Println("[ERROR] Failed to read packet:", err) + } + if size < 8 { + fmt.Println("error") + continue + } + + device.RoutineReceiveIncoming(buf, size, device.Sd4soc) + } } diff --git a/client/debug/dumpfile-client-real.pcapng b/client/debug/dumpfile-client-real.pcapng deleted file mode 100644 index 377a793..0000000 Binary files a/client/debug/dumpfile-client-real.pcapng and /dev/null differ diff --git a/client/debug/dumpfile-client-virtual.pcapng b/client/debug/dumpfile-client-virtual.pcapng deleted file mode 100644 index 19c0e84..0000000 Binary files a/client/debug/dumpfile-client-virtual.pcapng and /dev/null differ diff --git a/client/debug/dumpfile-client.pcapng b/client/debug/dumpfile-client.pcapng new file mode 100644 index 0000000..aeff622 Binary files /dev/null and b/client/debug/dumpfile-client.pcapng differ diff --git a/client/exec/run.go b/client/exec/run.go deleted file mode 100644 index d35f217..0000000 --- a/client/exec/run.go +++ /dev/null @@ -1,60 +0,0 @@ -package exec - -import ( - "fmt" - "os" - "strconv" - "time" - - "github.com/GotoRen/echoman/client/chorus" - "github.com/GotoRen/echoman/client/internal" - "github.com/GotoRen/echoman/client/internal/logger" - "github.com/joho/godotenv" -) - -func Run() { - var err error - t := time.NewTicker(time.Second * 1) - loadConf() - - device := internal.NewDevice(os.Getenv("LOCAL_INTERFACE")) - fmt.Println("[INFO] IP version:", device.EnvIPver) - fmt.Println("[INFO] Local IPv4:", device.LocalIPv4) - - device.NewPeer() - fmt.Println("[INFO] Peer IPv4:", device.Peer.PeerEndPoint.IP) - - device.CreateTunInterface() - fmt.Println("[INFO] TUN IPv4:", device.Tun.VIP) - - // Create Application - //=======================================================================// - device.CreateDescriptor() - defer device.Close() - - device.ChorusPort, err = strconv.Atoi(os.Getenv("CHROUS_PORT")) - if err != nil { - fmt.Println(err) - } - - // Listens on ports used by applications (chorus) that use the overlay network. - chorus.Listen(device.Tun.VIP, device.ChorusPort) - //=======================================================================// - - go device.RoutineSequentialReceiver() - go device.RoutineSequentialSender() - - for { - <-t.C - device.NewChorusUDPPacket() // If you want to generate UDP packets, please uncomment here. - } -} - -func loadConf() { - err := godotenv.Load() - if err != nil { - logger.LogErr("Error loading .env file", "error", err) - } - - logger.InitZap() -} diff --git a/client/go.mod b/client/go.mod index ad194d4..7a6f868 100644 --- a/client/go.mod +++ b/client/go.mod @@ -4,18 +4,13 @@ go 1.19 require ( github.com/google/gopacket v1.1.19 - github.com/google/uuid v1.3.0 github.com/joho/godotenv v1.4.0 - github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/vishvananda/netlink v1.1.0 go.uber.org/zap v1.23.0 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 - golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 ) require ( - github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444 // indirect + golang.org/x/sys v0.0.0-20190412213103-97732733099d // indirect ) diff --git a/client/go.sum b/client/go.sum index a9cd6b2..a2cd1a4 100644 --- a/client/go.sum +++ b/client/go.sum @@ -4,22 +4,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= -github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= @@ -36,11 +28,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444 h1:/d2cWp6PSamH4jDPFLyO150psQdqvtoNX8Zjg3AQ31g= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/client/chorus/listen.go b/client/internal/client.go similarity index 60% rename from client/chorus/listen.go rename to client/internal/client.go index b8b9967..d4f6ca6 100644 --- a/client/chorus/listen.go +++ b/client/internal/client.go @@ -1,4 +1,4 @@ -package chorus +package internal import ( "net" @@ -6,13 +6,13 @@ import ( "github.com/GotoRen/echoman/client/internal/logger" ) -func portConf(li string, lp int) (*net.UDPConn, error) { +func portConf(lp uint16) (*net.UDPConn, error) { udpAddr := &net.UDPAddr{ - IP: net.ParseIP(li), - Port: lp, + IP: net.IPv4zero.To4(), + Port: int(lp), } - c, err := net.ListenUDP("udp", udpAddr) + c, err := net.ListenUDP("udp4", udpAddr) if err != nil { return nil, err } @@ -27,8 +27,8 @@ func listenUDPPort(c *net.UDPConn) { } } -func Listen(appIP string, appPort int) { - conn, err := portConf(appIP, appPort) +func (device *Device) ListenClient() { + conn, err := portConf(device.LocalUDPPort) if err != nil { logger.LogErr("Failed to create connection", "error", err) } diff --git a/client/internal/conn.go b/client/internal/conn.go deleted file mode 100644 index 6d10b54..0000000 --- a/client/internal/conn.go +++ /dev/null @@ -1,31 +0,0 @@ -package internal - -import ( - "net" - "strconv" -) - -// makeUDPConnection makes UDP Connection. -func makeUDPConnection(dstPort int) (connUDP *net.UDPConn, err error) { - udpAddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(dstPort)) - if err != nil { - return nil, err - } - - connUDP, err = net.ListenUDP("udp", udpAddr) - if err != nil { - return nil, err - } - - return connUDP, nil -} - -// CreateUDPConnection creates a UDP connection with the peer node. -func CreateUDPConnection(peerUDPport int) (*net.UDPConn, error) { - connUDP, err := makeUDPConnection(peerUDPport) - if err != nil { - return nil, err - } - - return connUDP, nil -} diff --git a/client/internal/device.go b/client/internal/device.go index 6e1cf35..fec2263 100644 --- a/client/internal/device.go +++ b/client/internal/device.go @@ -5,109 +5,50 @@ import ( "net" "os" "strconv" - "strings" "github.com/GotoRen/echoman/client/internal/logger" ) type Device struct { - EnvIPver int - - // Real Interface IfIndex *net.Interface LocalIPv4 net.IP - LocalIPv6 net.IP + LocalMAC net.HardwareAddr LocalUDPPort uint16 - - socket struct { - sd4soc int - rv4soc int - } - - // TUN/TAP Interface - Tun struct { - Device *TunInterface - VIP string - mtu int32 - } - - ChorusPort int - - Peer *Peer + Sd4soc int + Rv4soc int + Peer *Peer } -// NewDevice defines device information. -func NewDevice(intf string) (device *Device) { - envIPVer, err := strconv.Atoi(os.Getenv("VIRTUAL_IP_TYPE")) +func GetDeviceInfo(intf string) (device *Device) { + interfaces, err := net.Interfaces() if err != nil { - logger.LogErr("Unable to get VIRTUAL_IP_TYPE", "error", err) + log.Fatal(err) } - interfaces, err := net.Interfaces() + netInterface, err := net.InterfaceByName(intf) if err != nil { log.Fatal(err) } localIPv4addr := getServerIPv4(intf, interfaces) if localIPv4addr == nil { - logger.LogErr("IPv4 address is empty", "error", err) - } - - device = &Device{ - EnvIPver: envIPVer, - LocalIPv4: localIPv4addr, - } - - return device -} - -// CreateTunInterface creates a TUN/TAP interface. -func (device *Device) CreateTunInterface() { - var err error - - vipv4, vipv4prefix := generateVirtualIPv4() - vipv6, vipv6prefix := generateVirtualIPv6() - - switch device.EnvIPver { - case 4: - device.Tun.Device, err = NewTunInterface(os.Getenv("TUN_INTERFACE_NAME"), vipv4.String(), vipv4prefix) - if err != nil { - logger.LogErr("Unable to create IPv4 TUN/TAP interface", "error", err) - } - case 6: - device.Tun.Device, err = NewTunInterface(os.Getenv("TUN_INTERFACE_NAME"), vipv6.String(), vipv6prefix) - if err != nil { - logger.LogErr("Unable to create IPv6 TUN/TAP interface", "error", err) - } - } - - if err := device.Tun.Device.Up(); err != nil { - logger.LogErr("Failed to Tunnel up", "error", err) + log.Fatal("[ERROR]: ipv4 address is empty.") } - device.Tun.VIP = device.Tun.Device.address[:strings.Index(device.Tun.Device.address, "/")] -} - -// CreateDescriptor creates socket descriptor. -func (device *Device) CreateDescriptor() { - var err error + localMACaddr := netInterface.HardwareAddr - // send socket - device.socket.sd4soc, err = SendIPv4RawSocket(device.Tun.VIP) + localUDPport, err := strconv.Atoi(os.Getenv("LOCAL_UDP_PORT")) if err != nil { - logger.LogErr("Failed to open send IPv4 raw socket", "error", err) + logger.LogErr("Type conversion failed", "error", err) } - // receive socket - device.socket.rv4soc, err = RecvIPv4RawSocket(device.Tun.VIP) - if err != nil { - logger.LogErr("Failed to open receive IPv4 raw socket", "error", err) + device = &Device{ + IfIndex: netInterface, + LocalIPv4: localIPv4addr, + LocalMAC: localMACaddr, + LocalUDPPort: uint16(localUDPport), + Peer: GerPeerInfo(), } -} -// Close closes device's queue, workers. -func (device *Device) Close() { - closeRawSocket(device.socket.sd4soc, "send IPv4") - closeRawSocket(device.socket.rv4soc, "receive IPv4") - logger.LogDebug("Device closed") + return device } diff --git a/client/internal/handle_icmpv4.go b/client/internal/handle_icmpv4.go new file mode 100644 index 0000000..a3e29c9 --- /dev/null +++ b/client/internal/handle_icmpv4.go @@ -0,0 +1,74 @@ +package internal + +import ( + "github.com/GotoRen/echoman/client/internal/logger" + "github.com/google/gopacket" + golayers "github.com/google/gopacket/layers" +) + +func (device *Device) NewICMPv4Packet() []byte { + ether := golayers.Ethernet{ + DstMAC: device.Peer.PeerMAC, + SrcMAC: device.LocalMAC, + EthernetType: golayers.EthernetTypeIPv4, + } + + ip := golayers.IPv4{ + Version: 4, + TOS: 0, + Length: 0, + Id: 0, + FragOffset: 0, + TTL: 255, + Protocol: golayers.IPProtocolICMPv4, + Checksum: 0, + SrcIP: device.LocalIPv4, + DstIP: device.Peer.PeerIPv4, + } + + icmpv4 := golayers.ICMPv4{ + TypeCode: golayers.CreateICMPv4TypeCode(8, 0), + Checksum: 0, + Id: 10, + Seq: 1, + } + + // ICMPv4 Max payload length: 48 byte + req_data := []byte{ + 34, 43, 67, 99, + 0, 0, 0, 0, + 49, 129, 5, 0, + 0, 0, 0, 0, + 16, 17, 18, 19, + 21, 22, 23, 24, + 25, 26, 27, 28, + 29, 30, 31, 32, + 33, 34, 35, 36, + 37, 38, 39, 40, + 41, 42, 43, 44, + 45, 46, 47, 48, + 49, 50, 51, 52, + 53, 54, 55, 20, + } + + options := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + + buffer := gopacket.NewSerializeBuffer() + + if err := gopacket.SerializeLayers(buffer, options, + ðer, + &ip, + &icmpv4, + gopacket.Payload(req_data), + ); err != nil { + logger.LogErr("Serialize error", "error", err) + return nil + } + + outgoingPacket := buffer.Bytes() + + return outgoingPacket +} diff --git a/client/internal/handle_udp.go b/client/internal/handle_udp.go new file mode 100644 index 0000000..e42a82b --- /dev/null +++ b/client/internal/handle_udp.go @@ -0,0 +1,61 @@ +package internal + +import ( + "github.com/GotoRen/echoman/client/internal/logger" + "github.com/google/gopacket" + golayers "github.com/google/gopacket/layers" +) + +func (device *Device) NewUDPPacket() []byte { + ether := golayers.Ethernet{ + DstMAC: device.Peer.PeerMAC, + SrcMAC: device.LocalMAC, + EthernetType: golayers.EthernetTypeIPv4, + } + + ip := golayers.IPv4{ + Version: 4, + TOS: 0, + Length: 0, + Id: 0, + FragOffset: 0, + TTL: 255, + Protocol: golayers.IPProtocolUDP, + Checksum: 0, + SrcIP: device.LocalIPv4, + DstIP: device.Peer.PeerIPv4, + } + + udp := golayers.UDP{ + SrcPort: golayers.UDPPort(device.LocalUDPPort), + DstPort: golayers.UDPPort(device.Peer.PeerUDPPort), + } + data := []byte("Ping") + + // calculating checksum + if err := udp.SetNetworkLayerForChecksum(&ip); err != nil { + logger.LogErr("udp set error", "error", err) + return nil + } + + options := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + + buffer := gopacket.NewSerializeBuffer() + + if err := gopacket.SerializeLayers(buffer, options, + ðer, + &ip, + &udp, + gopacket.Payload(data), + ); err != nil { + logger.LogErr("Serialize error", "error", err) + return nil + } + + outgoingPacket := buffer.Bytes() + + return outgoingPacket +} diff --git a/client/internal/handler.go b/client/internal/handler.go new file mode 100644 index 0000000..17875d0 --- /dev/null +++ b/client/internal/handler.go @@ -0,0 +1,29 @@ +package internal + +import ( + "fmt" + "log" + + "github.com/GotoRen/echoman/client/layers" +) + +func (device *Device) GenerateICMPv4Packet(fd int) { + icmpv4Packet := device.NewICMPv4Packet() + // layers.UnmarshalIPv4Packet(icmpv4Packet) + // layers.UnmarshalICMPv4Packet(icmpv4Packet) + if err := SendEtherPacket(fd, icmpv4Packet); err != nil { + log.Fatal(err) + } else { + fmt.Println("[INFO] Received ICMPv4 echo request") + } +} + +func (device *Device) GenerateUDPPacket(fd int) { + udpPacket := device.NewUDPPacket() + // layers.UnmarshalIPv4Packet(udpPacket) + // layers.UnmarshalICMPv4Packet(udpPacket) + layers.DebugUDPMessage(udpPacket) + if err := SendEtherPacket(fd, udpPacket); err != nil { + log.Fatal(err) + } +} diff --git a/client/internal/peer.go b/client/internal/peer.go index be4ab34..57d0431 100644 --- a/client/internal/peer.go +++ b/client/internal/peer.go @@ -9,32 +9,29 @@ import ( ) type Peer struct { - // Network information - PeerEndPoint net.UDPAddr - - // UDP connection - ConnUDP *net.UDPConn + PeerIPv4 net.IP + PeerMAC net.HardwareAddr + PeerUDPPort uint16 } -// NewPeer creates peer network information and UDP connections. -func (device *Device) NewPeer() { +func GerPeerInfo() (peer *Peer) { peerIPv4addr := net.ParseIP(os.Getenv("PEER_IPV4_ADDRESS")) - peerUDPport, err := strconv.Atoi(os.Getenv("PEER_UDP_PORT")) + peerMACaddr, err := net.ParseMAC(os.Getenv("PEER_MAC_ADDRESS")) if err != nil { - logger.LogErr("Type conversion failed", "error", err) + logger.LogErr("MAC address parse error", "error", err) } - connUDP, err := CreateUDPConnection(peerUDPport) + peerUDPport, err := strconv.Atoi(os.Getenv("PEER_UDP_PORT")) if err != nil { - logger.LogErr("Unable to establish UDP connection", "error", err) + logger.LogErr("Type conversion failed", "error", err) } - device.Peer = &Peer{ - PeerEndPoint: net.UDPAddr{ - IP: peerIPv4addr, - Port: peerUDPport, - }, - ConnUDP: connUDP, + peer = &Peer{ + PeerIPv4: peerIPv4addr, + PeerMAC: peerMACaddr, + PeerUDPPort: uint16(peerUDPport), } + + return peer } diff --git a/client/internal/raw_socket.go b/client/internal/raw_socket.go index e44bcec..f99cb46 100644 --- a/client/internal/raw_socket.go +++ b/client/internal/raw_socket.go @@ -1,6 +1,7 @@ package internal import ( + "log" "net" "syscall" @@ -11,10 +12,6 @@ func htons(host uint16) uint16 { return (host&0xff)<<8 | (host >> 8) } -// ========================================================================= // -// L2 socket -// ========================================================================= // - // etherSendSock creates a new send socket for IPv4 ethernet packet. func etherSendSock(intfIndex *net.Interface) (int, error) { fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_IP))) @@ -57,7 +54,7 @@ func etherRecvSock(intfIndex *net.Interface) (int, error) { return fd, nil } -// SendEtherPacket uses a send socket to send an ether packet. +// SendEtherPacket uses a send socket to send an Ethernete packet. func SendEtherPacket(fd int, b []byte) error { if _, err := syscall.Write(fd, b); err != nil { return err @@ -66,82 +63,19 @@ func SendEtherPacket(fd int, b []byte) error { return nil } -// RecvEtherPacket uses a receive socket to receive an ether packet. -func RecvEtherPacket(fd int, b []byte) error { - if _, err := syscall.Read(fd, b); err != nil { - return err - } - - return nil -} - -// ========================================================================= // -// L3 socket -// ========================================================================= // +// CreateDescriptor creates socket descriptor. +func (device *Device) CreateDescriptor() { + var err error -// SendIPv4RawSocket creates a raw socket for sending IPv4 packet. -func SendIPv4RawSocket(dip string) (int, error) { - fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) + // send socket + device.Sd4soc, err = etherSendSock(device.IfIndex) if err != nil { - return -1, err - } - - ip := net.ParseIP(dip) - addr := syscall.SockaddrInet4{ - Addr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + logger.LogErr("Failed to open send IPv4 raw socket", "error", err) } - if err = syscall.Bind(fd, &addr); err != nil { - return -1, err - } - - return fd, nil -} - -// RecvIPv4RawSocket creates a raw socket for receiving IPv4 packet. -func RecvIPv4RawSocket(sip string) (int, error) { - fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) + // receive socket + device.Rv4soc, err = etherRecvSock(device.IfIndex) if err != nil { - return -1, err - } - - ip := net.ParseIP(sip) - addr := syscall.SockaddrInet4{ - Addr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, - } - - if err = syscall.Bind(fd, &addr); err != nil { - return -1, err - } - - return fd, nil -} - -// SendPacket4 sends IPv4 packet. -func SendPacket4(fd int, b []byte, dip []byte) error { - addr := syscall.SockaddrInet4{ - Addr: [4]byte{dip[0], dip[1], dip[2], dip[3]}, - } - - if err := syscall.Sendto(fd, b, 0, &addr); err != nil { - return err - } - - return nil -} - -// ========================================================================= // -// Socket controller -// ========================================================================= // - -// closeRawSocket closes opening file descriptor. -func closeRawSocket(fd int, fdType string) { - if fd == -1 { - return - } - - if err := syscall.Close(fd); err != nil { - message := "Failed to close the " + fdType + " Raw socket" - logger.LogErr(message, "error", err) + log.Fatal(err) } } diff --git a/client/internal/read_packet.go b/client/internal/read_packet.go index 7599deb..b024ce1 100644 --- a/client/internal/read_packet.go +++ b/client/internal/read_packet.go @@ -1,86 +1,111 @@ package internal import ( + "encoding/binary" "fmt" - "os" "github.com/GotoRen/echoman/client/internal/logger" + "github.com/GotoRen/echoman/client/layers" "github.com/google/gopacket" golayers "github.com/google/gopacket/layers" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" ) -func (device *Device) ReadIPv4Packet(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) +func (device *Device) RoutineReceiveIncoming(buf []byte, size, sd4soc int) { + packet := gopacket.NewPacket(buf[:size], golayers.LayerTypeEthernet, gopacket.Default) + eh := &layers.EtherHeader{ + DstMacAddr: buf[layers.DstMACAddrOffset : layers.DstMACAddrOffset+layers.DstMacLength], + SrcMacAddr: buf[layers.SrcMACAddrOffset : layers.SrcMACAddrOffset+layers.SrcMacLength], + ProtoType: binary.BigEndian.Uint16(buf[layers.Protocoloffset : layers.Protocoloffset+layers.ProtocolTypeLength]), + } - icmpv4Layer := packet.Layer(golayers.LayerTypeICMPv4) - if icmpv4Layer != nil { - icmpv4 := icmpv4Layer.(*golayers.ICMPv4) - switch icmpv4.TypeCode.Type() { - case golayers.ICMPv4TypeEchoRequest: - fmt.Println("[DEBUG] Received ICMPv4 echo request") - case golayers.ICMPv4TypeEchoReply: - fmt.Println("[DEBUG] Received ICMPv4 echo replay") - default: - logger.LogErr("Unknown ICMPv4 packet type", "error", icmpv4.TypeCode.Type()) - os.Exit(1) + switch eh.ProtoType { + case layers.EthTypeIpv4: + if len(buf[layers.Etherlen:size]) < ipv4.HeaderLen { + logger.LogErr("Received IPv4 packet is too small", "error", len(buf[layers.Etherlen:size])) } - } - udpLayer := packet.Layer(golayers.LayerTypeUDP) - if udpLayer != nil { - udp := udpLayer.(*golayers.UDP) - switch udp.DstPort.LayerType() { - case golayers.LayerTypeDHCPv4: - fmt.Println("[DEBUG] Received DHCPv4 packet") - case golayers.LayerTypeDNS: - fmt.Println(("[DEBUG] Received DNS A record packet")) - default: - logger.LogErr("Unknown IPv4 UDP packet type", "error", udp.DstPort.LayerType()) - os.Exit(1) + icmpv4Layer := packet.Layer(golayers.LayerTypeICMPv4) + if icmpv4Layer != nil { + icmpv4 := icmpv4Layer.(*golayers.ICMPv4) + switch icmpv4.TypeCode.Type() { + case golayers.ICMPv4TypeDestinationUnreachable: + fmt.Println("[ERROR] Received ICMPv4 Destination Unreachable") + case golayers.ICMPv4TypeEchoRequest: + // Do nothing. + case golayers.ICMPv4TypeEchoReply: + fmt.Println("[INFO] Received ICMPv4 echo replay") + default: + logger.LogErr("Unknown ICMPv4 packet type", "error", icmpv4.TypeCode.Type()) + } } - } -} -func (device *Device) ReadIPv6Packet(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv6, gopacket.Default) + udpLayer := packet.Layer(golayers.LayerTypeUDP) + if udpLayer != nil { + udp := udpLayer.(*golayers.UDP) + switch udp.DstPort.LayerType() { + case golayers.LayerTypeDHCPv4: + logger.LogDebug("Received DHCPv4 packet", "DHCPv4", udp.DstPort.LayerType()) + case golayers.LayerTypeDNS: + logger.LogDebug("Received DNS A record packet", "DNS", udp.DstPort.LayerType()) + default: + if udp.DstPort == golayers.UDPPort(device.Peer.PeerUDPPort) { + // Echoman requst + // Do nothing. + } else if udp.DstPort == golayers.UDPPort(device.LocalUDPPort) { + // Echoman response + layers.DebugUDPMessage(buf) + } else { + logger.LogErr("Unknown IPv4 UDP packet type", "error", udp.DstPort) + } + } + } + case layers.EthTypeIpv6: + if len(buf[layers.Etherlen:size]) < ipv6.HeaderLen { + logger.LogErr("Received IPv6 packet is too small", "error", len(buf[layers.Etherlen:size])) + } - icmpv6Layer := packet.Layer(golayers.LayerTypeICMPv6) - if icmpv6Layer != nil { - icmpv6 := icmpv6Layer.(*golayers.ICMPv6) - switch icmpv6.TypeCode.Type() { - case golayers.ICMPv6TypeDestinationUnreachable: - fmt.Println("[DEBUG] Received ICMPv6 Destination Unreachable") - case golayers.ICMPv6TypeEchoRequest: - fmt.Println("[DEBUG] Received ICMPv6 echo request") - case golayers.ICMPv6TypeEchoReply: - fmt.Println("[DEBUG] Received ICMPv6 echo replay") - case golayers.ICMPv6TypeRouterSolicitation: - fmt.Println("[DEBUG] Received Router Solicitation") - case golayers.ICMPv6TypeRouterAdvertisement: - fmt.Println("[DEBUG] Received Router Advertisement") - case golayers.ICMPv6TypeNeighborSolicitation: - fmt.Println("[DEBUG] Received Neighbor Solicitation") - case golayers.ICMPv6TypeNeighborAdvertisement: - fmt.Println("[DEBUG] Received Neighbor Advertisement") - case golayers.ICMPv6TypeMLDv2MulticastListenerReportMessageV2: - fmt.Println("[DEBUG] Received Multicast ListenerReport MessageV2") - default: - logger.LogErr("Unknown ICMPv6 packet type", "error", icmpv6.TypeCode.Type()) - os.Exit(1) + icmpv6Layer := packet.Layer(golayers.LayerTypeICMPv6) + if icmpv6Layer != nil { + icmpv6 := icmpv6Layer.(*golayers.ICMPv6) + switch icmpv6.TypeCode.Type() { + case golayers.ICMPv6TypeDestinationUnreachable: + fmt.Println("[ERROR] Received ICMPv6 Destination Unreachable") + case golayers.ICMPv6TypeEchoRequest: + logger.LogDebug("Received ICMPv6 echo request", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeEchoReply: + logger.LogDebug("Received ICMPv6 echo replay", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeRouterSolicitation: + logger.LogDebug("Received Router Solicitation", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeRouterAdvertisement: + logger.LogDebug("Received Router Advertisement", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeNeighborSolicitation: + logger.LogDebug("Received Neighbor Solicitation", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeNeighborAdvertisement: + logger.LogDebug("Received Neighbor Advertisement", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeMLDv2MulticastListenerReportMessageV2: + logger.LogDebug("Received Multicast ListenerReport MessageV2", "ICMPv6", icmpv6.TypeCode.Type()) + default: + logger.LogErr("Unknown ICMPv6 packet type", "error", icmpv6.TypeCode.Type()) + } } - } - udpLayer := packet.Layer(golayers.LayerTypeUDP) - if udpLayer != nil { - udp := udpLayer.(*golayers.UDP) - switch udp.DstPort.LayerType() { - case golayers.LayerTypeDHCPv6: - fmt.Println(("[DEBUG] Received DHCPv6 packet")) - case golayers.LayerTypeDNS: - fmt.Println(("[DEBUG] Received DNS AAAA record packet")) - default: - logger.LogErr("Unknown IPv6 UDP packet type", "error", udp.DstPort.LayerType()) - os.Exit(1) + udpLayer := packet.Layer(golayers.LayerTypeUDP) + if udpLayer != nil { + udp := udpLayer.(*golayers.UDP) + switch udp.DstPort.LayerType() { + case golayers.LayerTypeDHCPv6: + logger.LogDebug("Received DHCPv6 packet", "DHCPv6", udp.DstPort.LayerType()) + case golayers.LayerTypeDNS: + logger.LogDebug("Received DNS AAAA record packet", "DNS", udp.DstPort.LayerType()) + default: + logger.LogErr("Unknown IPv6 UDP packet type", "error", udp.DstPort.LayerType()) + } } + case layers.EthTypeArp: + fmt.Println("[INFO] Received ARP packet") + default: + logger.LogErr("Detect unknown protocol version", "error", eh.ProtoType) } } diff --git a/client/internal/receive.go b/client/internal/receive.go deleted file mode 100644 index 18143d1..0000000 --- a/client/internal/receive.go +++ /dev/null @@ -1,60 +0,0 @@ -package internal - -import ( - "fmt" - - "github.com/GotoRen/echoman/client/internal/logger" - "github.com/GotoRen/echoman/client/layers" - "golang.org/x/net/ipv4" - "golang.org/x/net/ipv6" -) - -// RoutineSequentialReceiver forwards the peer's packets obtained from -// the real interface to the virtual interface. -func (device *Device) RoutineSequentialReceiver() { - for { - buf := make([]byte, 1500) - size, _, err := device.Peer.ConnUDP.ReadFrom(buf) - if err != nil { - logger.LogErr("Failed to receive UDP packet", "error", err) - } - - if size == 0 { - logger.LogErr("Received packet is too small", "error", size) - continue - } - - switch buf[0] >> 4 { - case ipv4.Version: - if len(buf) < ipv4.HeaderLen { - logger.LogErr("Received IPv4 packet is too small", "error", len(buf)) - buf = nil - continue - } - // dst := buf[layers.IPv4offsetDst : layers.IPv4offsetDst+net.IPv4len] - // fmt.Println("[INFO] Peer IPv4 Address", dst) - - layers.DebugUDPMessage(buf) // Receive debug - - if _, err := device.Tun.Device.Tun.Write(buf); err != nil { - logger.LogErr("Failed to write to tun/tap interface", "error", err) - } - - case ipv6.Version: - if len(buf) < ipv6.HeaderLen { - logger.LogErr("Received IPv6 packet is too small", "error", len(buf)) - buf = nil - continue - } - // dst := buf[layers.IPv6offsetDst : layers.IPv6offsetDst+net.IPv6len] - // fmt.Println("[INFO] Peer IPv6 Address", dst) - - if _, err := device.Tun.Device.Tun.Write(buf); err != nil { - logger.LogErr("Failed to write to tun/tap interface", "error", err) - } - - default: - fmt.Println("ip version error") - } - } -} diff --git a/client/internal/send.go b/client/internal/send.go deleted file mode 100644 index 6d89d5e..0000000 --- a/client/internal/send.go +++ /dev/null @@ -1,79 +0,0 @@ -package internal - -import ( - "net" - "os" - - "github.com/GotoRen/echoman/client/chorus" - "github.com/GotoRen/echoman/client/internal/logger" - "github.com/GotoRen/echoman/client/layers" - golayers "github.com/google/gopacket/layers" - "golang.org/x/net/ipv4" - "golang.org/x/net/ipv6" -) - -// RoutineSequentialSender sends packets obtained from a virtual interface to the peer. -func (device *Device) RoutineSequentialSender() { - for { - buf := make([]byte, 1500) - size, err := device.Tun.Device.Read(buf) - if err != nil { - logger.LogErr("Failed to receive virtual IP packet", "error", err) - } - - if size == 0 { - logger.LogErr("Received packet is too small", "error", size) - continue - } - - switch buf[0] >> 4 { - case ipv4.Version: - if len(buf) < ipv4.HeaderLen { - logger.LogErr("Received IPv4 packet is too small", "error", len(buf)) - buf = nil - continue - } - // fmt.Println("[DEBUG] Received IPv4 packet from TUN/TAP", buf[:size]) - // dst := buf[layers.IPv4offsetDst : layers.IPv4offsetDst+net.IPv4len] - - layers.DebugUDPMessage(buf) // Send debug - - if _, err := device.Peer.ConnUDP.WriteToUDP(buf, &device.Peer.PeerEndPoint); err != nil { - logger.LogErr("Failed to write to real interface", "error", err) - } - - case ipv6.Version: - if len(buf) < ipv6.HeaderLen { - logger.LogErr("Received IPv6 packet is too small", "error", len(buf)) - buf = nil - continue - } - // fmt.Println("[DEBUG] Received IPv6 packet from TUN/TAP", buf[:size]) - // dst := buf[layers.IPv6offsetDst : layers.IPv6offsetDst+net.IPv6len] - - if _, err := device.Peer.ConnUDP.WriteToUDP(buf, &device.Peer.PeerEndPoint); err != nil { - logger.LogErr("Failed to write virtual IPv6 Packet", "error", err) - } - - default: - logger.LogErr("ip version error", "error", buf[0]>>4) - } - } -} - -// NewChorusUDPPacket sends the generated UDP packet. -func (device *Device) NewChorusUDPPacket() { - srcVIPv4 := net.ParseIP(device.Tun.VIP).To4() - dstVIPv4 := net.ParseIP(os.Getenv("ECHOMAN_SERVER_IPV4_TUN")).To4() - chorusPort := golayers.UDPPort(device.ChorusPort) - - udpPacket, err := chorus.GenerateUDPRequestPacket(srcVIPv4, dstVIPv4, chorusPort) - if err != nil { - logger.LogErr("Failed to generate udp packet", "error", err) - } - - // write to the tun/tap interface - if err := SendPacket4(device.socket.sd4soc, udpPacket, dstVIPv4); err != nil { - logger.LogErr("Failed to write to tun/tap interface", "error", err) - } -} diff --git a/client/internal/tun.go b/client/internal/tun.go deleted file mode 100644 index 797225e..0000000 --- a/client/internal/tun.go +++ /dev/null @@ -1,154 +0,0 @@ -// Package internal contains the TCP/UDP connection, -// setups TUN/TAP Device, handles DNS packets. -package internal - -import ( - "fmt" - "os/exec" - "runtime" - - "github.com/GotoRen/echoman/client/internal/logger" - "github.com/songgao/water" - "github.com/vishvananda/netlink" - "golang.org/x/xerrors" -) - -// TunInterface manages Tunnel device. -type TunInterface struct { - Tun *water.Interface - address string -} - -// NewTunInterface returns Tunnel device. -func NewTunInterface(name string, address string, prefix string) (*TunInterface, error) { - addr := address + prefix - - switch runtime.GOOS { - case "linux": - config := water.Config{ - DeviceType: water.TUN, - } - config.Name = name - - ifce, err := water.New(config) - if err != nil { - return nil, err - } - - iface := &TunInterface{ - Tun: ifce, - address: addr, - } - - return iface, nil - - case "darwin": - ifce, err := water.New(water.Config{ - DeviceType: water.TUN, - }) - if err != nil { - return nil, err - } - - iface := &TunInterface{ - Tun: ifce, - address: addr, - } - - return iface, nil - - case "windows": - return nil, xerrors.Errorf("Windows is not supported") - default: - return nil, xerrors.Errorf("%s is not supported", runtime.GOOS) - } -} - -// Up function ups a virtual interface. -func (iface *TunInterface) Up() error { - switch runtime.GOOS { - case "linux": - out, err := execCmd("ip", []string{"addr", "add", iface.address, "dev", iface.Tun.Name()}) - logger.LogDebug("Add a Virtual Interface", "Virtual Interface", out) - - if err != nil { - logger.LogErr("ip command add fail", "error", err) - return err - } - - set, err := execCmd("ip", []string{"link", "set", "dev", iface.Tun.Name(), "up", "mtu", "1460"}) - logger.LogDebug("Up a Virtual Interface", "Virtual Interface", set) - - if err != nil { - logger.LogErr("ip command set fail", "error", err) - return err - } - - case "darwin": - out, err := execCmd("ifconfig", []string{iface.Tun.Name(), "up"}) - logger.LogDebug("Add a Virtual Interface", "Virtual Interface", out) - - if err != nil { - logger.LogErr("ifconfig fail", "error", err) - return err - } - - if tun, err := netlink.LinkByName(iface.Tun.Name()); err == nil { - addr, err := netlink.ParseAddr(iface.address) - if err != nil { - logger.LogErr("Unable to parse address", "error", err) - } - - if err := netlink.AddrAdd(tun, addr); err != nil { - logger.LogErr("Unable to add IP address to linked device", "error", err) - } - - // TODO: Change MTU - - logger.LogDebug("Check Virtual Interface Name", "Virtual Interface Name", iface.Tun.Name()) - logger.LogDebug("Check Virtual Interface Address", "Virtual Interface Address", iface.address) - } - - default: - logger.LogErr("unsupported", "error", runtime.GOOS) - logger.LogErr("unsupported", "error", runtime.GOARCH) - return fmt.Errorf("unsupported: %s %s", runtime.GOOS, runtime.GOARCH) - } - - return nil -} - -// Read function read the virtual interface. -func (iface *TunInterface) Read(buf []byte) (int, error) { - n, err := iface.Tun.Read(buf) - // Read Virtual Interface. - if err != nil { - logger.LogErr("Failed to read virtual interface", "error", err) - return 0, err - } - - return n, nil -} - -// Write function write the virtual interface. -func (iface *TunInterface) Write(buf []byte) (int, error) { - return iface.Tun.Write(buf) -} - -// Close function closes the virtual interface. -func (iface *TunInterface) Close() { - if err := iface.Tun.Close(); err != nil { - logger.LogErr("Failed to close virtual interface", "error", err) - } -} - -// execCmd executes the given arguments as a command. -func execCmd(cmd string, args []string) (string, error) { - execCmd := exec.Command(cmd, args...) - if err := execCmd.Run(); err != nil { - logger.LogErr("Unable to execute command ", "error", err.Error()) - return execCmd.String(), err - } - - return execCmd.String(), nil -} diff --git a/client/internal/vip.go b/client/internal/vip.go deleted file mode 100644 index 02dcd28..0000000 --- a/client/internal/vip.go +++ /dev/null @@ -1,86 +0,0 @@ -package internal - -import ( - "crypto/rand" - "encoding/binary" - "fmt" - "net" - "strings" - - "github.com/GotoRen/echoman/client/internal/logger" - "github.com/google/uuid" -) - -// rfc3330, rfc2544. -// Using the Documentation addresses and prefixes. -var VIPv4NetworkAddress string = "198.18.0.0/16" -var VIPv6NetworkAddress string = "2001:0db8:c0ff:ee00::/64" - -// getVIPv4NetworkAddressInfo returns information about virtual IPv4 overlay network. -func getVIPv4NetworkAddressInfo() (vipv4nwaddress net.IP, vipv4netmask net.IPMask, vipv4prefix string) { - _, ipnet, err := net.ParseCIDR(VIPv4NetworkAddress) - if err != nil { - logger.LogErr("Unable to get Virtual IPv4 network address", "error", err) - } - - vipv4nwaddress = ipnet.IP - vipv4netmask = ipnet.Mask - vipv4prefix = VIPv4NetworkAddress[strings.Index(VIPv4NetworkAddress, "/"):] - - return -} - -// getVIPv6NetworkAddressInfo returns information about virtual IPv6 overlay network. -func getVIPv6NetworkAddressInfo() (vipv6nwaddress net.IP, vipv6netmask net.IPMask, vipv6prefix string) { - _, ipnet, err := net.ParseCIDR(VIPv6NetworkAddress) - if err != nil { - logger.LogErr("Unable to get Virtual IPv4 network address", "error", err) - } - - vipv6nwaddress = ipnet.IP - vipv6netmask = ipnet.Mask - vipv6prefix = VIPv6NetworkAddress[strings.Index(VIPv6NetworkAddress, "/"):] - - return -} - -// generateVirtualIPv4 is to generate virtual IPv4 address. -func generateVirtualIPv4() (net.IP, string) { - vipv4nwaddress, _, vipv4prefix := getVIPv4NetworkAddressInfo() - b := make([]byte, 2) - -createVIPv4: - err := binary.Read(rand.Reader, binary.BigEndian, &b) - if err != nil { - logger.LogErr("Failed to generate random 2 bytes for VirtualIPv4", "error", err) - } - - vipv4 := net.IPv4(vipv4nwaddress[0], vipv4nwaddress[1], b[0], b[1]).To4() - - // TODO: If you use netip.Addr type you can use switch-case. - // 198.18.0.0/16 => network address or 198.18.255.255/16 => broadcast address - if vipv4.Equal(net.IPv4(vipv4nwaddress[0], vipv4nwaddress[1], 0x00, 0x00).To4()) || vipv4.Equal(net.IPv4(vipv4nwaddress[0], vipv4nwaddress[1], 0xff, 0xff).To4()) { - goto createVIPv4 - } - - return vipv4, vipv4prefix -} - -// generateVirtualIPv6 is to generate virtual IPv6 address. -func generateVirtualIPv6() (net.IP, string) { - vipv6nwaddress, _, vipv6prefix := getVIPv6NetworkAddressInfo() - - u, err := uuid.NewRandom() - if err != nil { - logger.LogErr("Failed to generate UUID for VirtualIPv6", "error", err) - } - - ub, err := u.MarshalBinary() - if err != nil { - logger.LogErr("For VirtualIPv6: Failed encode to binary format", "error", err) - } - - vipv6 := net.ParseIP(strings.Replace(vipv6nwaddress.String(), "::", fmt.Sprintf(":%x:%x:%x:%x", ub[8:10], ub[10:12], ub[12:14], ub[14:16]), 1)) - - return vipv6, vipv6prefix -} diff --git a/client/layers/icmpv4.go b/client/layers/icmpv4.go index 092a20f..c0f450e 100644 --- a/client/layers/icmpv4.go +++ b/client/layers/icmpv4.go @@ -39,7 +39,7 @@ const ( ) func UnmarshalICMPv4Packet(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) icmpv4Layer := packet.Layer(golayers.LayerTypeICMPv4) if icmpv4Layer != nil { icmpv4, _ := icmpv4Layer.(*golayers.ICMPv4) @@ -54,7 +54,7 @@ func UnmarshalICMPv4Packet(b []byte) { } func DebugICMPv4Message(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) icmpv4Layer := packet.Layer(golayers.LayerTypeICMPv4) if icmpv4Layer != nil { icmpv4, _ := icmpv4Layer.(*golayers.ICMPv4) diff --git a/client/layers/ipv4.go b/client/layers/ipv4.go index f85ed4a..c77147b 100644 --- a/client/layers/ipv4.go +++ b/client/layers/ipv4.go @@ -2,7 +2,6 @@ package layers import ( "fmt" - "net" "github.com/google/gopacket" golayers "github.com/google/gopacket/layers" @@ -41,15 +40,13 @@ const ( DstIPv4Length = 4 ) -// IPv4 offset length. const ( - IPv4offsetTotalLength = 2 // IPv4offsetPayloadLength is IPv4 offset payload length. - IPv4offsetSrc = 12 // IPv4offsetSrc is IPv4 offset src length. - IPv4offsetDst = IPv4offsetSrc + net.IPv4len // IPv4offsetDst is IPv4 offset dst length. + SrcIPv4AddrOffset = 26 + DstIPv4AddrOffset = 30 ) func UnmarshalIPv4Packet(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) ipv4Layer := packet.Layer(golayers.LayerTypeIPv4) if ipv4Layer != nil { ipv4, _ := ipv4Layer.(*golayers.IPv4) diff --git a/client/layers/ipv6.go b/client/layers/ipv6.go deleted file mode 100644 index eab3021..0000000 --- a/client/layers/ipv6.go +++ /dev/null @@ -1,10 +0,0 @@ -package layers - -import "net" - -// IPv6 offset length. -const ( - IPv6offsetPayloadLength = 4 // IPv6offsetPayloadLength is IPv6 offset payload length. - IPv6offsetSrc = 8 // IPv6offsetSrc is IPv6 offset src length. - IPv6offsetDst = IPv6offsetSrc + net.IPv6len // IPv6offsetDst is IPv6 offset dst length. -) diff --git a/client/layers/udp.go b/client/layers/udp.go index ace08bf..bb68f73 100644 --- a/client/layers/udp.go +++ b/client/layers/udp.go @@ -13,12 +13,12 @@ const ( ) const ( - SrcUDPPortOffset = 20 - DstUDPPortOffset = 22 + SrcUDPPortOffset = 34 + DstUDPPortOffset = 36 ) func UnmarshalUDPPacket(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) udpLayer := packet.Layer(golayers.LayerTypeUDP) if udpLayer != nil { udp, _ := udpLayer.(*golayers.UDP) @@ -34,7 +34,7 @@ func UnmarshalUDPPacket(b []byte) { } func DebugUDPMessage(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) udpLayer := packet.Layer(golayers.LayerTypeUDP) if udpLayer != nil { udp, _ := udpLayer.(*golayers.UDP) diff --git a/compose.yaml b/compose.yaml index e645072..f845115 100644 --- a/compose.yaml +++ b/compose.yaml @@ -9,11 +9,10 @@ services: networks: echoman: ipv4_address: 10.0.3.95 - ipv6_address: fde4:db8::0395 privileged: true restart: always sysctls: - net.ipv6.conf.all.disable_ipv6: "0" # enable + net.ipv6.conf.all.disable_ipv6: "1" tty: true volumes: - type: bind @@ -31,11 +30,10 @@ services: networks: echoman: ipv4_address: 10.0.3.96 - ipv6_address: fde4:db8::0396 privileged: true restart: always sysctls: - net.ipv6.conf.all.disable_ipv6: "0" # enable + net.ipv6.conf.all.disable_ipv6: "1" tty: true volumes: - type: bind @@ -51,6 +49,5 @@ networks: driver: bridge ipam: config: - - subnet: 10.0.3.0/22 - - subnet: fde4:db8::/8 - enable_ipv6: true + - subnet: 10.0.3.0/24 + enable_ipv6: false diff --git a/docs/01_information.md b/docs/01_information.md deleted file mode 100644 index 4bc1c92..0000000 --- a/docs/01_information.md +++ /dev/null @@ -1,31 +0,0 @@ -# Information: Network design - -## Default Information - -- Real Network range: `10.0.3.0/22` / `fde4:db8::/8` -- Overlay Network range: `198.18.0.0/16` / `2001:0db8:c0ff:ee00::/64` - -| Device | Information | -| :--- | ---: | -| Echoman Server IPv4 address | `10.0.3.95/22` | -| Echoman Server IPv6 address | `fde4:db8::0395/8` | -| Echoman Server UDP connection port | `30000` | -| Echoman Server TUN/TAP interface name | `echoman_tun` | -| Echoman Server Virtual IPv4 address | `198.18.9.10/16` | -| Echoman Server Virtual IPv6 address | `2001:db8:c0ff:ee00:0910:0910:0910:0910/64` | - -| Device | Information | -| :--- | ---: | -| Echoman Client IPv4 address | `10.0.3.96/22` | -| Echoman Client IPv4 address | `fde4:db8::0396/8` | -| Echoman Client UDP connection port | `30000` | -| Echoman Client TUN/TAP interface name | `echoman_tun` | -| Echoman Client Virtual IPv4 address | `198.18.x.x/16` | -| Echoman Client Virtual IPv6 address | `2001:db8:c0ff:ee00:xxxx:xxxx:xxxx:xxxx/64` | - -## TUN/TAP を介したecho -- ICMPv4 -ICMPv4.png - -- UDPv4 -UDPv4.png diff --git a/docs/02_application.md b/docs/02_application.md deleted file mode 100644 index d4da0a5..0000000 --- a/docs/02_application.md +++ /dev/null @@ -1,55 +0,0 @@ -# Application: An application that communicates over the overlay network - -## 🎶 Chorus: echoman test application -- Chorusは、echomanを使用したオーバーレイネットワーク通信をテストするための検証アプリケーション。 -- ポート番号は、それぞれ仮想インターフェースにバインドされた`30910 (UDP)`番を使用する。 -- UDP Request packet (`Ping`) をサーバに送信すると、クライアントにUDP Response packet (`Pong`) が返される。 - -## Design -``` -Client Server -+-------------------------------------+ +-------------------------------------+ -| +--------------+ | | +--------------+ | -| | App (Chorus) | | | | App (Chorus) | | -| +--------------+ | | +--------------+ | -| 198.18.x.x:30910 | | 198.18.9.10:30910 | -| | | | | | -| +--------------+ +--------------+ | | +--------------+ +--------------+ | -| | TUN/TAP | | Real I/F | | UDP tunnel | | Real I/F | | TUN/TAP | | -| | 198.18.x.x |---| 10.0.3.96 |=|============|=| 10.0.3.95 |---| 198.18.9.10 | | -| +--------------+ +--------------+ | | +--------------+ +--------------+ | -| 0.0.0.0:30000 | | 0.0.0.0:30000 | -+-------------------------------------+ +-------------------------------------+ -``` - -## README !! -- "TUN - Application" 間のパケットを操作するのがめんどくさい👊 - - クライアントの動作 - - Chorusは直接、仮想インターフェースに対して Rawsocket を開き、パケットを直接書き込む - - サーバの動作 - - まず、宛先パケットが `198.18.9.10:30910 (Chorus packet)` であるかどうかを判断する - - 次に、実インターフェースから受信したパケットを仮想インターフェースに対してそのまま書き込む - - 最後に、仮想インターフェースへの書き込みに成功すると、UDPレスポンスパケットを生成して、実インターフェースに書き込む -- ソースコード -```go -/************************************************************************************* - * README: description for Chorus.app * -************************************************************************************** - * Checking the TUN -> Application packet flow using source code is complicated. - * - For the time being, I will check with wireshark.app. - * Thefore, if the write to TUN succeeds, we generate and return a response message. - * - Write the message generated at this time directly to the Real interface. - * ### Judgment method ### - * - If the destination is "198.18.9.10:30910", judge it as chorus.app and return the message. - * - And, return a response to the UDP packet received from the client. -*************************************************************************************/ -if net.ParseIP(device.Tun.VIP).To4().Equal(dstIP) && golayers.UDPPort(uint16(device.ChorusPort)) == dstPort { - logger.LogDebug("Receive chorus message", "chrous", "success") - res := chorus.GenerateUDPResponsePacket(buf) - if _, err := device.Peer.ConnUDP.WriteToUDP(res, &device.Peer.PeerEndPoint); err != nil { - logger.LogErr("[Failed] Send chorus message", "error", err) - } else { - logger.LogDebug("Send chorus message", "chrous", "success") - } -} -``` diff --git a/docs/03_usage.md b/docs/03_usage.md deleted file mode 100644 index 103c63f..0000000 --- a/docs/03_usage.md +++ /dev/null @@ -1,48 +0,0 @@ -# Usage: How to start echoman - -## Requirement -- **⚠️WARNING**:https://docs.docker.com/desktop/previous-versions/3.x-mac/#new-9 - -> First version of docker compose (as an alternative to the existing docker-compose). Supports some basic commands but not the complete functionality of docker-compose yet. - -| Languages / Frameworks | Version | -| :--- | ---: | -| Golang | 1.19.1 | -| Docker Desktop | 4.12.0 | -| docker | 20.10.17 | -| docker-compose | 1.29.2 | - -## Usage -```sh -### envをコピー -$ cp server/.env{.sample,} -$ cp client/.env{.sample,} - -### .envの中身を環境に合わせて書き換える -※注意: Docker networkではMACアドレスはランダムに生成されます - -### docker-composeを起動 -$ make up - -### 実行 ---- -### 1枚目のTerminal: Echoman server を起動 -$ make exec/server -# make run - -### 2枚目のTerminal: Echoman client を起動 -$ make exec/client -# make run ---- -``` - -## UDPパケットの生成 -- デフォルトでUDPパケットを生成しない場合、コメントアウトして下さい。 -> ./echoman/client/exec/run.go - -```go -for { - <-t.C - device.NewChorusUDPPacket() // If you want to generate UDP packets, please uncomment here. -} -``` diff --git a/docs/04_appendix.md b/docs/04_appendix.md deleted file mode 100644 index 5f07ffd..0000000 --- a/docs/04_appendix.md +++ /dev/null @@ -1,66 +0,0 @@ -# Appendix: About sockets - -## Standard socket / Raw socket -socket.png - -- ソケット関数 - -| args | syscall | -| :--- | :---: | -| Domain | `AF_PACKET` | -| Type | `SOCK_RAW` | -| Protocols | `IPPROTO_RAW` | - -- L3~ をRawsocketで操作 -```go -// ### 送信ソケットの生成 -func SendIPv4RawSocket(dip string) (int, error) { - fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) - if err != nil { - return -1, err - } - - ip := net.ParseIP(dip) - addr := syscall.SockaddrInet4{ - Addr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, - } - - if err = syscall.Bind(fd, &addr); err != nil { - return -1, err - } - - return fd, nil -} - -// 受信ソケットの生成 -func RecvIPv4RawSocket(sip string) (int, error) { - fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) - if err != nil { - return -1, err - } - - ip := net.ParseIP(sip) - addr := syscall.SockaddrInet4{ - Addr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, - } - - if err = syscall.Bind(fd, &addr); err != nil { - return -1, err - } - - return fd, nil -} - -// IPv4パケット(L3~)の送信 -func SendPacket4(fd int, b []byte, dip []byte) error { - addr := syscall.SockaddrInet4{ - Addr: [4]byte{dip[0], dip[1], dip[2], dip[3]}, - } - - if err := syscall.Sendto(fd, b, 0, &addr); err != nil { - return err - } - - return nil -} -``` diff --git a/docs/img/icmpv4.png b/docs/img/icmpv4.png deleted file mode 100644 index f7f0cda..0000000 Binary files a/docs/img/icmpv4.png and /dev/null differ diff --git a/docs/img/socket.png b/docs/img/socket.png deleted file mode 100644 index 94fcb1d..0000000 Binary files a/docs/img/socket.png and /dev/null differ diff --git a/docs/img/udpv4.png b/docs/img/udpv4.png deleted file mode 100644 index df7bc82..0000000 Binary files a/docs/img/udpv4.png and /dev/null differ diff --git a/server/.env.sample b/server/.env.sample index 9fe4089..92af0b2 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -1,15 +1,8 @@ -DEBUG_MODE=true -VIRTUAL_IP_TYPE=4 - LOCAL_INTERFACE=eth0 +LOCAL_UDP_PORT=30005 PEER_IPV4_ADDRESS=10.0.3.96 -PEER_IPV6_ADDRESS=fde4:db8::0396 -PEER_UDP_PORT=30000 - -TUN_INTERFACE_NAME=echoman_tun +PEER_MAC_ADDRESS=02:42:0a:00:03:60 +PEER_UDP_PORT=30006 -ECHOMAN_SERVER_IPV4_TUN=198.18.9.10 -ECHOMAN_SERVER_IPV6_TUN=2001:db8:c0ff:ee00:0910:0910:0910:0910 - -CHROUS_PORT=30910 +DEBUG_MODE=true diff --git a/server/Makefile b/server/Makefile index f6b0246..8c36964 100644 --- a/server/Makefile +++ b/server/Makefile @@ -8,8 +8,7 @@ GODOC=$(GOCMD)doc GOLANGCI=golangci-lint DUMPCMD=tcpdump -INTERFACE_REAL=eth0 -INTERFACE_VIRTUAL=echoman_tun +INTERFACE=eth0 @@ -43,17 +42,11 @@ coverage: ## coverage go test -coverprofile=cover.out ./... go tool cover -html=cover.out -o cover.html -dump-real: ## dump packet - $(DUMPCMD) -i ${INTERFACE_REAL} +dump: ## dump packet + $(DUMPCMD) -i ${INTERFACE} -dump-virtual: ## dump packet - $(DUMPCMD) -i ${INTERFACE_VIRTUAL} - -dump-real/out: ## dump packet and output to file - $(DUMPCMD) -i ${INTERFACE_REAL} -w ./debug/dumpfile-server-real.pcapng - -dump-virtual/out: ## dump packet and output to file - $(DUMPCMD) -i ${INTERFACE_VIRTUAL} -w ./debug/dumpfile-server-virtual.pcapng +dump/out: ## dump packet and output to file + $(DUMPCMD) -i ${INTERFACE} -w ./debug/dumpfile-server.pcapng listen: ## check listen port lsof -i -P diff --git a/server/chorus/generate_response.go b/server/chorus/generate_response.go deleted file mode 100644 index aafbf53..0000000 --- a/server/chorus/generate_response.go +++ /dev/null @@ -1,121 +0,0 @@ -package chorus - -import ( - "encoding/binary" - - "github.com/GotoRen/echoman/server/internal/logger" - "github.com/GotoRen/echoman/server/layers" - "github.com/google/gopacket" - golayers "github.com/google/gopacket/layers" -) - -// GenerateUDPResponsePacket generates udp response packet. -func GenerateUDPResponsePacket(req []byte) []byte { - data := []byte("Pong") - - // swap source and destination information - srcIPv4Addr := req[layers.IPv4offsetDst : layers.IPv4offsetDst+layers.DstIPv4Length] - dstIPv4Addr := req[layers.IPv4offsetSrc : layers.IPv4offsetSrc+layers.SrcIPv4Length] - srcPort := binary.BigEndian.Uint16(req[layers.DstUDPPortOffset : layers.DstUDPPortOffset+layers.DstUDPLength]) - dstPort := binary.BigEndian.Uint16(req[layers.SrcUDPPortOffset : layers.SrcUDPPortOffset+layers.SrcUDPLength]) - - ip := golayers.IPv4{ - Version: 4, - Protocol: golayers.IPProtocolUDP, - SrcIP: srcIPv4Addr, - DstIP: dstIPv4Addr, - } - - udp := golayers.UDP{ - SrcPort: golayers.UDPPort(srcPort), - DstPort: golayers.UDPPort(dstPort), - } - - // fmt.Println("[DEBUG] srcIPv4Addr:", srcIPv4Addr) - // fmt.Println("[DEBUG] dstIPv4Addr:", dstIPv4Addr) - // fmt.Println("[DEBUG] srcPort:", srcPort) - // fmt.Println("[DEBUG] dstPort:", dstPort) - - // calculating checksum - if err := udp.SetNetworkLayerForChecksum(&ip); err != nil { - logger.LogErr("udp set error", "error", err) - return nil - } - - options := gopacket.SerializeOptions{ - ComputeChecksums: true, - FixLengths: true, - } - - buffer := gopacket.NewSerializeBuffer() - - if err := gopacket.SerializeLayers(buffer, options, - &ip, - &udp, - gopacket.Payload(data), - ); err != nil { - logger.LogErr("Serialize error", "error", err) - return nil - } - - outgoingPacket := buffer.Bytes() - - return outgoingPacket -} - -// func NewICMPv4ReplayPacket(req []byte) []byte { -// // swap source and destination information -// dstMacAddr := net.HardwareAddr(req[layers.SrcMACAddrOffset : layers.SrcMACAddrOffset+layers.SrcMacLength]) -// srcMacAddr := net.HardwareAddr(req[layers.DstMACAddrOffset : layers.DstMACAddrOffset+layers.DstMacLength]) -// srcIPv4Addr := req[layers.DstIPv4AddrOffset : layers.DstIPv4AddrOffset+layers.DstIPv4Length] -// dstIPv4Addr := req[layers.SrcIPv4AddrOffset : layers.SrcIPv4AddrOffset+layers.SrcIPv4Length] - -// res_data := req[layers.ICMPv4Dataoffset : layers.ICMPv4Dataoffset+layers.ICMPv4DataLength+layers.ICMPv4TimeStampLength] - -// ether := golayers.Ethernet{ -// DstMAC: dstMacAddr, -// SrcMAC: srcMacAddr, -// EthernetType: golayers.EthernetTypeIPv4, -// } - -// ip := golayers.IPv4{ -// Version: 4, -// TOS: 0, -// Length: 0, -// Id: 0, -// FragOffset: 0, -// TTL: 255, -// Protocol: golayers.IPProtocolICMPv4, -// Checksum: 0, -// SrcIP: srcIPv4Addr, -// DstIP: dstIPv4Addr, -// } - -// icmpv4 := golayers.ICMPv4{ -// TypeCode: golayers.CreateICMPv4TypeCode(0, 0), -// Checksum: 0, -// Id: 10, -// Seq: 1, -// } - -// options := gopacket.SerializeOptions{ -// ComputeChecksums: true, -// FixLengths: true, -// } - -// buffer := gopacket.NewSerializeBuffer() - -// if err := gopacket.SerializeLayers(buffer, options, -// ðer, -// &ip, -// &icmpv4, -// gopacket.Payload(res_data), -// ); err != nil { -// logger.LogErr("Serialize error", "error", err) -// return nil -// } - -// outgoingPacket := buffer.Bytes() - -// return outgoingPacket -// } diff --git a/server/cmd/main.go b/server/cmd/main.go index e9ce4d0..b1a2f66 100644 --- a/server/cmd/main.go +++ b/server/cmd/main.go @@ -1,9 +1,50 @@ package main import ( - "github.com/GotoRen/echoman/server/exec" + "fmt" + "os" + "syscall" + + "github.com/joho/godotenv" + + "github.com/GotoRen/echoman/server/internal" + "github.com/GotoRen/echoman/server/internal/logger" ) +func init() { + err := godotenv.Load() + if err != nil { + logger.LogErr("Error loading .env file", "error", err) + } +} + func main() { - exec.Run() + logger.InitZap() + + device := internal.GetDeviceInfo(os.Getenv("LOCAL_INTERFACE")) + fmt.Println("[INFO] Local Interface Information:", device.IfIndex) + fmt.Println("[INFO] Local IPv4 Address:", device.LocalIPv4) + fmt.Println("[INFO] Local Hardware Address:", device.LocalMAC) + fmt.Println("[INFO] Peer IPv4 Address:", device.Peer.PeerIPv4) + fmt.Println("[INFO] Peer Hardware Address:", device.Peer.PeerMAC) + + device.CreateDescriptor() + defer syscall.Close(device.Sd4soc) + defer syscall.Close(device.Rv4soc) + + device.ListenServer() + + for { + buf := make([]byte, 1500) + size, _, err := syscall.Recvfrom(device.Rv4soc, buf, 0) + if err != nil { + fmt.Println("[ERROR] Failed to read packet:", err) + } + if size < 8 { + fmt.Println("error") + continue + } + + device.RoutineReceiveIncoming(buf, size, device.Sd4soc) + } } diff --git a/server/debug/dumpfile-server-real.pcapng b/server/debug/dumpfile-server-real.pcapng deleted file mode 100644 index f5b3639..0000000 Binary files a/server/debug/dumpfile-server-real.pcapng and /dev/null differ diff --git a/server/debug/dumpfile-server-virtual.pcapng b/server/debug/dumpfile-server-virtual.pcapng deleted file mode 100644 index 17f4dac..0000000 Binary files a/server/debug/dumpfile-server-virtual.pcapng and /dev/null differ diff --git a/server/debug/dumpfile-server.pcapng b/server/debug/dumpfile-server.pcapng new file mode 100644 index 0000000..0931d0f Binary files /dev/null and b/server/debug/dumpfile-server.pcapng differ diff --git a/server/exec/run.go b/server/exec/run.go deleted file mode 100644 index ede3d6e..0000000 --- a/server/exec/run.go +++ /dev/null @@ -1,54 +0,0 @@ -package exec - -import ( - "fmt" - "os" - "strconv" - - "github.com/GotoRen/echoman/server/chorus" - "github.com/GotoRen/echoman/server/internal" - "github.com/GotoRen/echoman/server/internal/logger" - "github.com/joho/godotenv" -) - -func Run() { - var err error - loadConf() - - device := internal.NewDevice(os.Getenv("LOCAL_INTERFACE")) - fmt.Println("[INFO] IP version:", device.EnvIPver) - fmt.Println("[INFO] Local IPv4:", device.LocalIPv4) - - device.NewPeer() - fmt.Println("[INFO] Peer IPv4:", device.Peer.PeerEndPoint.IP) - - device.CreateTunInterface() - fmt.Println("[INFO] TUN IPv4:", device.Tun.VIP) - - // Create Application - //=======================================================================// - device.ChorusPort, err = strconv.Atoi(os.Getenv("CHROUS_PORT")) - if err != nil { - fmt.Println(err) - } - - // Listens on ports used by applications (chorus) that use the overlay network. - chorus.Listen(device.Tun.VIP, device.ChorusPort) - //=======================================================================// - - go device.RoutineSequentialReceiver() - go device.RoutineSequentialSender() - - for { - // make the main routine wait - } -} - -func loadConf() { - err := godotenv.Load() - if err != nil { - logger.LogErr("Error loading .env file", "error", err) - } - - logger.InitZap() -} diff --git a/server/go.mod b/server/go.mod index 62b0816..799f01f 100644 --- a/server/go.mod +++ b/server/go.mod @@ -4,18 +4,13 @@ go 1.19 require ( github.com/google/gopacket v1.1.19 - github.com/google/uuid v1.3.0 github.com/joho/godotenv v1.4.0 - github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 - github.com/vishvananda/netlink v1.1.0 go.uber.org/zap v1.23.0 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 - golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 ) require ( - github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444 // indirect + golang.org/x/sys v0.0.0-20190412213103-97732733099d // indirect ) diff --git a/server/go.sum b/server/go.sum index a9cd6b2..a2cd1a4 100644 --- a/server/go.sum +++ b/server/go.sum @@ -4,22 +4,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8= -github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= @@ -36,11 +28,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444 h1:/d2cWp6PSamH4jDPFLyO150psQdqvtoNX8Zjg3AQ31g= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/server/internal/checksum.go b/server/internal/checksum.go deleted file mode 100644 index 920793a..0000000 --- a/server/internal/checksum.go +++ /dev/null @@ -1,117 +0,0 @@ -package internal - -import ( - "encoding/binary" - "fmt" - "reflect" - "strconv" -) - -func paddingZero(arr []byte) []byte { - padarr := append(arr, 0x00) - return padarr -} - -func printByteArr(arr []byte) string { - var str string - for _, v := range arr { - //fmt.Printf("%#x ", v) - str += fmt.Sprintf("%x ", v) - } - return str -} - -func sum3BytetoLength(arr []byte) uint64 { - length, _ := strconv.ParseUint(fmt.Sprintf("%x", arr), 16, 16) - return length -} - -func sumByteArr(arr []byte) uint { - var sum uint - for i := 0; i < len(arr); i++ { - if i%2 == 0 { - sum += uint(binary.BigEndian.Uint16(arr[i:])) - } - } - //fmt.Printf("0x%x : %b\n", sum, sum) - return sum -} - -func checktoByteArr(value interface{}) { - rv := reflect.ValueOf(value) - rt := rv.Type() - var arr []byte - - for i := 0; i < rv.NumField(); i++ { - field := rt.Field(i) - - b := rv.Field(i).Interface().([]byte) - arr = append(arr, b...) - fmt.Printf("%s : %x\n", field.Name, b) - } -} - -// 各構造体のフィールドが持つbyteをflatな配列にする -func toByteArr(value interface{}) []byte { - rv := reflect.ValueOf(value) - //rt := rv.Type() - var arr []byte - - for i := 0; i < rv.NumField(); i++ { - //field := rt.Field(i) - //fmt.Printf("%s\n", field.Name) - b := rv.Field(i).Interface().([]byte) - arr = append(arr, b...) - } - - return arr -} - -func toByteLen(value interface{}) uint16 { - rv := reflect.ValueOf(value) - var arr []byte - - for i := 0; i < rv.NumField(); i++ { - b := rv.Field(i).Interface().([]byte) - arr = append(arr, b...) - } - - return uint16(len(arr)) -} - -func UintTo2byte(data uint16) []byte { - b := make([]byte, 2) - binary.BigEndian.PutUint16(b, data) - return b -} - -func UintTo3byte(data uint32) []byte { - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, data) - return b[1:] -} - -func UintTo4byte(data uint32) []byte { - b := make([]byte, 4) - binary.BigEndian.PutUint32(b, data) - return b -} - -func checksum(sum uint) []byte { - // https://el.jibun.atmarkit.co.jp/hiro/2013/07/tcp-f933.html - // 22DA6 - 20000 + 2 = 2DA8となり、2DA8をビット反転 - val := sum - (sum>>16)<<16 + (sum >> 16) ^ 0xffff - return UintTo2byte(uint16(val)) -} - -func SumbyteArr(arr []byte) uint { - return sumByteArr(arr) -} - -func CalcChecksum(sum uint) []byte { - return checksum(sum) -} - -func ToPacket(value interface{}) []byte { - return toByteArr(value) -} diff --git a/server/internal/conn.go b/server/internal/conn.go deleted file mode 100644 index 6d10b54..0000000 --- a/server/internal/conn.go +++ /dev/null @@ -1,31 +0,0 @@ -package internal - -import ( - "net" - "strconv" -) - -// makeUDPConnection makes UDP Connection. -func makeUDPConnection(dstPort int) (connUDP *net.UDPConn, err error) { - udpAddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(dstPort)) - if err != nil { - return nil, err - } - - connUDP, err = net.ListenUDP("udp", udpAddr) - if err != nil { - return nil, err - } - - return connUDP, nil -} - -// CreateUDPConnection creates a UDP connection with the peer node. -func CreateUDPConnection(peerUDPport int) (*net.UDPConn, error) { - connUDP, err := makeUDPConnection(peerUDPport) - if err != nil { - return nil, err - } - - return connUDP, nil -} diff --git a/server/internal/device.go b/server/internal/device.go index b7687a7..e73c5db 100644 --- a/server/internal/device.go +++ b/server/internal/device.go @@ -5,82 +5,50 @@ import ( "net" "os" "strconv" - "strings" "github.com/GotoRen/echoman/server/internal/logger" ) type Device struct { - EnvIPver int - - // Real Interface IfIndex *net.Interface LocalIPv4 net.IP - LocalIPv6 net.IP + LocalMAC net.HardwareAddr LocalUDPPort uint16 - - socket struct { - sd4soc int - rv4soc int - } - - // TUN/TAP Interface - Tun struct { - Device *TunInterface - VIP string - mtu int32 - } - - ChorusPort int - - Peer *Peer + Sd4soc int + Rv4soc int + Peer *Peer } -// NewDevice defines device information. -func NewDevice(intf string) (device *Device) { - envIPVer, err := strconv.Atoi(os.Getenv("VIRTUAL_IP_TYPE")) +func GetDeviceInfo(intf string) (device *Device) { + interfaces, err := net.Interfaces() if err != nil { - logger.LogErr("Unable to get VIRTUAL_IP_TYPE", "error", err) + log.Fatal(err) } - interfaces, err := net.Interfaces() + netInterface, err := net.InterfaceByName(intf) if err != nil { log.Fatal(err) } localIPv4addr := getServerIPv4(intf, interfaces) if localIPv4addr == nil { - logger.LogErr("IPv4 address is empty", "error", err) + log.Fatal("[ERROR]: ipv4 address is empty.") } - device = &Device{ - EnvIPver: envIPVer, - LocalIPv4: localIPv4addr, - } - - return device -} + localMACaddr := netInterface.HardwareAddr -// CreateTunInterface creates a TUN/TAP interface. -func (device *Device) CreateTunInterface() { - var err error - - switch device.EnvIPver { - case 4: - device.Tun.Device, err = NewTunInterface(os.Getenv("TUN_INTERFACE_NAME"), os.Getenv("ECHOMAN_SERVER_IPV4_TUN"), "/16") - if err != nil { - logger.LogErr("Failed to create Tunnel Interface Virtual IPv4", "error", err) - } - case 6: - device.Tun.Device, err = NewTunInterface(os.Getenv("TUN_INTERFACE_NAME"), os.Getenv("ECHOMAN_SERVER_IPV6_TUN"), "/64") - if err != nil { - logger.LogErr("Failed to create Tunnel Interface Virtual IPv6", "error", err) - } + localUDPport, err := strconv.Atoi(os.Getenv("LOCAL_UDP_PORT")) + if err != nil { + logger.LogErr("Type conversion failed", "error", err) } - if err := device.Tun.Device.Up(); err != nil { - logger.LogErr("Failed to Tunnel up", "error", err) + device = &Device{ + IfIndex: netInterface, + LocalIPv4: localIPv4addr, + LocalMAC: localMACaddr, + LocalUDPPort: uint16(localUDPport), + Peer: GerPeerInfo(), } - device.Tun.VIP = device.Tun.Device.address[:strings.Index(device.Tun.Device.address, "/")] + return device } diff --git a/server/internal/handle_icmpv4.go b/server/internal/handle_icmpv4.go new file mode 100644 index 0000000..ee5997e --- /dev/null +++ b/server/internal/handle_icmpv4.go @@ -0,0 +1,67 @@ +package internal + +import ( + "net" + + "github.com/GotoRen/echoman/server/internal/logger" + "github.com/GotoRen/echoman/server/layers" + "github.com/google/gopacket" + golayers "github.com/google/gopacket/layers" +) + +func NewICMPv4ReplayPacket(req []byte) []byte { + // ここで送信元と宛先の情報を入れ替える + dstMacAddr := net.HardwareAddr(req[layers.SrcMACAddrOffset : layers.SrcMACAddrOffset+layers.SrcMacLength]) + srcMacAddr := net.HardwareAddr(req[layers.DstMACAddrOffset : layers.DstMACAddrOffset+layers.DstMacLength]) + srcIPv4Addr := req[layers.DstIPv4AddrOffset : layers.DstIPv4AddrOffset+layers.DstIPv4Length] + dstIPv4Addr := req[layers.SrcIPv4AddrOffset : layers.SrcIPv4AddrOffset+layers.SrcIPv4Length] + + res_data := req[layers.ICMPv4Dataoffset : layers.ICMPv4Dataoffset+layers.ICMPv4DataLength+layers.ICMPv4TimeStampLength] + + ether := golayers.Ethernet{ + DstMAC: dstMacAddr, + SrcMAC: srcMacAddr, + EthernetType: golayers.EthernetTypeIPv4, + } + + ip := golayers.IPv4{ + Version: 4, + TOS: 0, + Length: 0, + Id: 0, + FragOffset: 0, + TTL: 255, + Protocol: golayers.IPProtocolICMPv4, + Checksum: 0, + SrcIP: srcIPv4Addr, + DstIP: dstIPv4Addr, + } + + icmpv4 := golayers.ICMPv4{ + TypeCode: golayers.CreateICMPv4TypeCode(0, 0), + Checksum: 0, + Id: 10, + Seq: 1, + } + + options := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + + buffer := gopacket.NewSerializeBuffer() + + if err := gopacket.SerializeLayers(buffer, options, + ðer, + &ip, + &icmpv4, + gopacket.Payload(res_data), + ); err != nil { + logger.LogErr("Serialize error", "error", err) + return nil + } + + outgoingPacket := buffer.Bytes() + + return outgoingPacket +} diff --git a/server/internal/handle_udp.go b/server/internal/handle_udp.go new file mode 100644 index 0000000..ea05200 --- /dev/null +++ b/server/internal/handle_udp.go @@ -0,0 +1,73 @@ +package internal + +import ( + "encoding/binary" + "net" + + "github.com/GotoRen/echoman/server/internal/logger" + "github.com/GotoRen/echoman/server/layers" + "github.com/google/gopacket" + golayers "github.com/google/gopacket/layers" +) + +func NewUDPResponsePacket(req []byte) []byte { + // ここで送信元と宛先の情報を入れ替える + dstMacAddr := net.HardwareAddr(req[layers.SrcMACAddrOffset : layers.SrcMACAddrOffset+layers.SrcMacLength]) + srcMacAddr := net.HardwareAddr(req[layers.DstMACAddrOffset : layers.DstMACAddrOffset+layers.DstMacLength]) + srcIPv4Addr := req[layers.DstIPv4AddrOffset : layers.DstIPv4AddrOffset+layers.DstIPv4Length] + dstIPv4Addr := req[layers.SrcIPv4AddrOffset : layers.SrcIPv4AddrOffset+layers.SrcIPv4Length] + srcPort := binary.BigEndian.Uint16(req[layers.DstUDPPortOffset : layers.DstUDPPortOffset+layers.DstUDPLength]) + dstPort := binary.BigEndian.Uint16(req[layers.SrcUDPPortOffset : layers.SrcUDPPortOffset+layers.SrcUDPLength]) + + ether := golayers.Ethernet{ + DstMAC: dstMacAddr, + SrcMAC: srcMacAddr, + EthernetType: golayers.EthernetTypeIPv4, + } + + ip := golayers.IPv4{ + Version: 4, + TOS: 0, + Length: 0, + Id: 0, + FragOffset: 0, + TTL: 255, + Protocol: golayers.IPProtocolUDP, + Checksum: 0, + SrcIP: srcIPv4Addr, + DstIP: dstIPv4Addr, + } + + udp := golayers.UDP{ + SrcPort: golayers.UDPPort(srcPort), + DstPort: golayers.UDPPort(dstPort), + } + data := []byte("Pong") + + // calculating checksum + if err := udp.SetNetworkLayerForChecksum(&ip); err != nil { + logger.LogErr("udp set error", "error", err) + return nil + } + + options := gopacket.SerializeOptions{ + ComputeChecksums: true, + FixLengths: true, + } + + buffer := gopacket.NewSerializeBuffer() + + if err := gopacket.SerializeLayers(buffer, options, + ðer, + &ip, + &udp, + gopacket.Payload(data), + ); err != nil { + logger.LogErr("Serialize error", "error", err) + return nil + } + + outgoingPacket := buffer.Bytes() + + return outgoingPacket +} diff --git a/server/internal/peer.go b/server/internal/peer.go index 7b49770..e34b1ea 100644 --- a/server/internal/peer.go +++ b/server/internal/peer.go @@ -9,32 +9,29 @@ import ( ) type Peer struct { - // Network information - PeerEndPoint net.UDPAddr - - // UDP connection - ConnUDP *net.UDPConn + PeerIPv4 net.IP + PeerMAC net.HardwareAddr + PeerUDPPort uint16 } -// NewPeer creates peer network information and UDP connections. -func (device *Device) NewPeer() { +func GerPeerInfo() (peer *Peer) { peerIPv4addr := net.ParseIP(os.Getenv("PEER_IPV4_ADDRESS")) - peerUDPport, err := strconv.Atoi(os.Getenv("PEER_UDP_PORT")) + peerMACaddr, err := net.ParseMAC(os.Getenv("PEER_MAC_ADDRESS")) if err != nil { - logger.LogErr("Type conversion failed", "error", err) + logger.LogErr("MAC address parse error", "error", err) } - connUDP, err := CreateUDPConnection(peerUDPport) + peerUDPport, err := strconv.Atoi(os.Getenv("PEER_UDP_PORT")) if err != nil { - logger.LogErr("Unable to establish UDP connection", "error", err) + logger.LogErr("Type conversion failed", "error", err) } - device.Peer = &Peer{ - PeerEndPoint: net.UDPAddr{ - IP: peerIPv4addr, - Port: peerUDPport, - }, - ConnUDP: connUDP, + peer = &Peer{ + PeerIPv4: peerIPv4addr, + PeerMAC: peerMACaddr, + PeerUDPPort: uint16(peerUDPport), } + + return peer } diff --git a/server/internal/raw_socket.go b/server/internal/raw_socket.go index 7f8c31b..89440f4 100644 --- a/server/internal/raw_socket.go +++ b/server/internal/raw_socket.go @@ -1,6 +1,7 @@ package internal import ( + "log" "net" "syscall" @@ -11,10 +12,6 @@ func htons(host uint16) uint16 { return (host&0xff)<<8 | (host >> 8) } -// ========================================================================= // -// L2 socket -// ========================================================================= // - // etherSendSock creates a new send socket for IPv4 ethernet packet. func etherSendSock(intfIndex *net.Interface) (int, error) { fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(syscall.ETH_P_IP))) @@ -57,7 +54,7 @@ func etherRecvSock(intfIndex *net.Interface) (int, error) { return fd, nil } -// SendEtherPacket uses a send socket to send an ether packet. +// SendEtherPacket uses a send socket to send an Ethernete packet. func SendEtherPacket(fd int, b []byte) error { if _, err := syscall.Write(fd, b); err != nil { return err @@ -66,82 +63,19 @@ func SendEtherPacket(fd int, b []byte) error { return nil } -// RecvEtherPacket uses a receive socket to receive an ether packet. -func RecvEtherPacket(fd int, b []byte) error { - if _, err := syscall.Read(fd, b); err != nil { - return err - } - - return nil -} - -// ========================================================================= // -// L3 socket -// ========================================================================= // +// CreateDescriptor creates socket descriptor. +func (device *Device) CreateDescriptor() { + var err error -// SendIPv4RawSocket creates a raw socket for sending IPv4 packet. -func SendIPv4RawSocket(dip string) (int, error) { - fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) + // send socket + device.Sd4soc, err = etherSendSock(device.IfIndex) if err != nil { - return -1, err - } - - ip := net.ParseIP(dip) - addr := syscall.SockaddrInet4{ - Addr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, + logger.LogErr("Failed to open send IPv4 raw socket", "error", err) } - if err = syscall.Bind(fd, &addr); err != nil { - return -1, err - } - - return fd, nil -} - -// RecvIPv4RawSocket creates a raw socket for receiving IPv4 packet. -func RecvIPv4RawSocket(sip string) (int, error) { - fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_RAW) + // receive socket + device.Rv4soc, err = etherRecvSock(device.IfIndex) if err != nil { - return -1, err - } - - ip := net.ParseIP(sip) - addr := syscall.SockaddrInet4{ - Addr: [4]byte{ip[0], ip[1], ip[2], ip[3]}, - } - - if err = syscall.Bind(fd, &addr); err != nil { - return -1, err - } - - return fd, nil -} - -// SendPacket4 sends IPv4 packet. -func SendPacket4(fd int, b []byte, dip []byte) error { - addr := syscall.SockaddrInet4{ - Addr: [4]byte{dip[0], dip[1], dip[2], dip[3]}, - } - - if err := syscall.Sendto(fd, b, 0, &addr); err != nil { - return err - } - - return nil -} - -// ========================================================================= // -// Socket controller -// ========================================================================= // - -// closeRawSocket closes opening file descriptor. -func closeRawSocket(fd int, fdType string) { - if fd == -1 { - return - } - - if err := syscall.Close(fd); err != nil { - message := "Failed to close the " + fdType + " Raw socket" - logger.LogErr(message, "error", err) + log.Fatal(err) } } diff --git a/server/internal/read_packet.go b/server/internal/read_packet.go index 15c6d1d..3d9882d 100644 --- a/server/internal/read_packet.go +++ b/server/internal/read_packet.go @@ -1,86 +1,121 @@ package internal import ( + "encoding/binary" "fmt" - "os" "github.com/GotoRen/echoman/server/internal/logger" + "github.com/GotoRen/echoman/server/layers" "github.com/google/gopacket" golayers "github.com/google/gopacket/layers" + "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" ) -func (device *Device) ReadIPv4Packet(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) +func (device *Device) RoutineReceiveIncoming(buf []byte, size, sd4soc int) { + packet := gopacket.NewPacket(buf[:size], golayers.LayerTypeEthernet, gopacket.Default) + eh := &layers.EtherHeader{ + DstMacAddr: buf[layers.DstMACAddrOffset : layers.DstMACAddrOffset+layers.DstMacLength], + SrcMacAddr: buf[layers.SrcMACAddrOffset : layers.SrcMACAddrOffset+layers.SrcMacLength], + ProtoType: binary.BigEndian.Uint16(buf[layers.Protocoloffset : layers.Protocoloffset+layers.ProtocolTypeLength]), + } - icmpv4Layer := packet.Layer(golayers.LayerTypeICMPv4) - if icmpv4Layer != nil { - icmpv4 := icmpv4Layer.(*golayers.ICMPv4) - switch icmpv4.TypeCode.Type() { - case golayers.ICMPv4TypeEchoRequest: - fmt.Println("[DEBUG] Received ICMPv4 echo request") - case golayers.ICMPv4TypeEchoReply: - fmt.Println("[DEBUG] Received ICMPv4 echo replay") - default: - logger.LogErr("Unknown ICMPv4 packet type", "error", icmpv4.TypeCode.Type()) - os.Exit(1) + switch eh.ProtoType { + case layers.EthTypeIpv4: + if len(buf[layers.Etherlen:size]) < ipv4.HeaderLen { + logger.LogErr("Received IPv4 packet is too small", "error", len(buf[layers.Etherlen:size])) } - } - udpLayer := packet.Layer(golayers.LayerTypeUDP) - if udpLayer != nil { - udp := udpLayer.(*golayers.UDP) - switch udp.DstPort.LayerType() { - case golayers.LayerTypeDHCPv4: - fmt.Println("[DEBUG] Received DHCPv4 packet") - case golayers.LayerTypeDNS: - fmt.Println(("[DEBUG] Received DNS A record packet")) - default: - logger.LogErr("Unknown IPv4 UDP packet type", "error", udp.DstPort.LayerType()) - os.Exit(1) + icmpv4Layer := packet.Layer(golayers.LayerTypeICMPv4) + if icmpv4Layer != nil { + icmpv4 := icmpv4Layer.(*golayers.ICMPv4) + switch icmpv4.TypeCode.Type() { + case golayers.ICMPv4TypeEchoRequest: + fmt.Println("[INFO] Received ICMPv4 echo request") + icmpv4res := NewICMPv4ReplayPacket(buf) + if err := SendEtherPacket(device.Sd4soc, icmpv4res); err != nil { + logger.LogErr("Filed to send ether pakcet", "error", err) + } else { + fmt.Println("[INFO] Generate ICMPv4 echo replay packet") + } + case golayers.ICMPv4TypeEchoReply: + fmt.Println("[INFO] Received ICMPv4 echo replay") + default: + logger.LogErr("Unknown ICMPv4 packet type", "error", icmpv4.TypeCode.Type()) + } } - } -} -func (device *Device) ReadIPv6Packet(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv6, gopacket.Default) + udpLayer := packet.Layer(golayers.LayerTypeUDP) + if udpLayer != nil { + udp := udpLayer.(*golayers.UDP) + switch udp.DstPort.LayerType() { + case golayers.LayerTypeDHCPv4: + logger.LogDebug("Received DHCPv4 packet", "DHCPv4", udp.DstPort.LayerType()) + case golayers.LayerTypeDNS: + logger.LogDebug("Received DNS A record packet", "DNS", udp.DstPort.LayerType()) + default: + if udp.DstPort == golayers.UDPPort(device.LocalUDPPort) { + // Echoman requst + layers.DebugUDPMessage(buf) + udpres := NewUDPResponsePacket(buf) + if err := SendEtherPacket(device.Sd4soc, udpres); err != nil { + logger.LogErr("Filed to send ether pakcet", "error", err) + } else { + layers.DebugUDPMessage(udpres) + } + } else if udp.DstPort == golayers.UDPPort(device.Peer.PeerUDPPort) { + // Echoman response + // Do nothing. + } else { + logger.LogErr("Unknown IPv4 UDP packet type", "error", udp.DstPort) + } + } + } + case layers.EthTypeIpv6: + if len(buf[layers.Etherlen:size]) < ipv6.HeaderLen { + logger.LogErr("Received IPv6 packet is too small", "error", len(buf[layers.Etherlen:size])) + } - icmpv6Layer := packet.Layer(golayers.LayerTypeICMPv6) - if icmpv6Layer != nil { - icmpv6 := icmpv6Layer.(*golayers.ICMPv6) - switch icmpv6.TypeCode.Type() { - case golayers.ICMPv6TypeDestinationUnreachable: - fmt.Println("[DEBUG] Received ICMPv6 Destination Unreachable") - case golayers.ICMPv6TypeEchoRequest: - fmt.Println("[DEBUG] Received ICMPv6 echo request") - case golayers.ICMPv6TypeEchoReply: - fmt.Println("[DEBUG] Received ICMPv6 echo replay") - case golayers.ICMPv6TypeRouterSolicitation: - fmt.Println("[DEBUG] Received Router Solicitation") - case golayers.ICMPv6TypeRouterAdvertisement: - fmt.Println("[DEBUG] Received Router Advertisement") - case golayers.ICMPv6TypeNeighborSolicitation: - fmt.Println("[DEBUG] Received Neighbor Solicitation") - case golayers.ICMPv6TypeNeighborAdvertisement: - fmt.Println("[DEBUG] Received Neighbor Advertisement") - case golayers.ICMPv6TypeMLDv2MulticastListenerReportMessageV2: - fmt.Println("[DEBUG] Received Multicast ListenerReport MessageV2") - default: - logger.LogErr("Unknown ICMPv6 packet type", "error", icmpv6.TypeCode.Type()) - os.Exit(1) + icmpv6Layer := packet.Layer(golayers.LayerTypeICMPv6) + if icmpv6Layer != nil { + icmpv6 := icmpv6Layer.(*golayers.ICMPv6) + switch icmpv6.TypeCode.Type() { + case golayers.ICMPv6TypeDestinationUnreachable: + fmt.Println("[ERROR] Received ICMPv6 Destination Unreachable") + case golayers.ICMPv6TypeEchoRequest: + logger.LogDebug("Received ICMPv6 echo request", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeEchoReply: + logger.LogDebug("Received ICMPv6 echo replay", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeRouterSolicitation: + logger.LogDebug("Received Router Solicitation", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeRouterAdvertisement: + logger.LogDebug("Received Router Advertisement", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeNeighborSolicitation: + logger.LogDebug("Received Neighbor Solicitation", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeNeighborAdvertisement: + logger.LogDebug("Received Neighbor Advertisement", "ICMPv6", icmpv6.TypeCode.Type()) + case golayers.ICMPv6TypeMLDv2MulticastListenerReportMessageV2: + logger.LogDebug("Received Multicast ListenerReport MessageV2", "ICMPv6", icmpv6.TypeCode.Type()) + default: + logger.LogErr("Unknown ICMPv6 packet type", "error", icmpv6.TypeCode.Type()) + } } - } - udpLayer := packet.Layer(golayers.LayerTypeUDP) - if udpLayer != nil { - udp := udpLayer.(*golayers.UDP) - switch udp.DstPort.LayerType() { - case golayers.LayerTypeDHCPv6: - fmt.Println(("[DEBUG] Received DHCPv6 packet")) - case golayers.LayerTypeDNS: - fmt.Println(("[DEBUG] Received DNS AAAA record packet")) - default: - logger.LogErr("Unknown IPv6 UDP packet type", "error", udp.DstPort.LayerType()) - os.Exit(1) + udpLayer := packet.Layer(golayers.LayerTypeUDP) + if udpLayer != nil { + udp := udpLayer.(*golayers.UDP) + switch udp.DstPort.LayerType() { + case golayers.LayerTypeDHCPv6: + logger.LogDebug("Received DHCPv6 packet", "DHCPv6", udp.DstPort.LayerType()) + case golayers.LayerTypeDNS: + logger.LogDebug("Received DNS AAAA record packet", "DNS", udp.DstPort.LayerType()) + default: + logger.LogErr("Unknown IPv6 UDP packet type", "error", udp.DstPort.LayerType()) + } } + case layers.EthTypeArp: + fmt.Println("[INFO] Received ARP packet") + default: + logger.LogErr("Detect unknown protocol version", "error", eh.ProtoType) } } diff --git a/server/internal/receive.go b/server/internal/receive.go deleted file mode 100644 index a294b3d..0000000 --- a/server/internal/receive.go +++ /dev/null @@ -1,89 +0,0 @@ -package internal - -import ( - "encoding/binary" - "net" - - "github.com/GotoRen/echoman/server/chorus" - "github.com/GotoRen/echoman/server/internal/logger" - "github.com/GotoRen/echoman/server/layers" - golayers "github.com/google/gopacket/layers" - "golang.org/x/net/ipv4" - "golang.org/x/net/ipv6" -) - -// RoutineSequentialReceiver forwards the peer's packets obtained from -// the real interface to the virtual interface. -func (device *Device) RoutineSequentialReceiver() { - for { - buf := make([]byte, 1500) - size, _, err := device.Peer.ConnUDP.ReadFrom(buf) - if err != nil { - logger.LogErr("Failed to receive UDP packet", "error", err) - } - - if size == 0 { - logger.LogErr("Received packet is too small", "error", size) - continue - } - - switch buf[0] >> 4 { - case ipv4.Version: - if len(buf) < ipv4.HeaderLen { - logger.LogErr("Received IPv4 packet is too small", "error", len(buf)) - buf = nil - continue - } - dstIP := net.IP(buf[layers.IPv4offsetDst : layers.IPv4offsetDst+net.IPv4len]).To4() - dstPort := golayers.UDPPort(binary.BigEndian.Uint16(buf[layers.DstUDPPortOffset : layers.DstUDPPortOffset+layers.DstUDPLength])) - - // fmt.Println("[DEBUG] Peer IPv4 Address", dstIP) - // fmt.Println("[DEBUG] Peer IPv4 Port:", dstPort) - // fmt.Println("[DEBUG] Chrous app IPv4:", net.ParseIP(device.Tun.VIP).To4()) - // fmt.Println("[DEBUG] Chrous app Port:", golayers.UDPPort(uint16(device.ChrousPort))) - - if _, err := device.Tun.Device.Tun.Write(buf); err != nil { - logger.LogErr("Failed to write to tun/tap interface", "error", err) - } else { - /************************************************************************************* - * README: description for Chorus.app * - ************************************************************************************** - * Checking the TUN -> Application packet flow using source code is complicated. - * - For the time being, I will check with wireshark.app. - * Thefore, if the write to TUN succeeds, we generate and return a response message. - * - Write the message generated at this time directly to the Real interface. - * ### Judgment method ### - * - If the destination is "198.18.9.10:30910", judge it as chorus.app and return the message. - * - And, return a response to the UDP packet received from the client. - *************************************************************************************/ - if net.ParseIP(device.Tun.VIP).To4().Equal(dstIP) && golayers.UDPPort(uint16(device.ChorusPort)) == dstPort { - logger.LogDebug("Receive chorus message", "chrous", "success") - res := chorus.GenerateUDPResponsePacket(buf) - if _, err := device.Peer.ConnUDP.WriteToUDP(res, &device.Peer.PeerEndPoint); err != nil { - logger.LogErr("[Failed] Send chorus message", "error", err) - } else { - logger.LogDebug("Send chorus message", "chrous", "success") - } - } - } - - case ipv6.Version: - if len(buf) < ipv6.HeaderLen { - logger.LogErr("Received IPv6 packet is too small", "error", len(buf)) - buf = nil - continue - } - // dst := buf[layers.IPv6offsetDst : layers.IPv6offsetDst+net.IPv6len] - // fmt.Println("[INFO] Peer IPv6 Address", dst) - - device.ReadIPv6Packet(buf) - - if _, err := device.Tun.Device.Tun.Write(buf); err != nil { - logger.LogErr("Failed to write to tun/tap interface", "error", err) - } - - default: - logger.LogErr("ip version error", "error", int(buf[0]>>4)) - } - } -} diff --git a/server/internal/send.go b/server/internal/send.go deleted file mode 100644 index 4307dd3..0000000 --- a/server/internal/send.go +++ /dev/null @@ -1,58 +0,0 @@ -package internal - -import ( - "fmt" - "os" - - "github.com/GotoRen/echoman/server/internal/logger" - "golang.org/x/net/ipv4" - "golang.org/x/net/ipv6" -) - -// RoutineSequentialSender sends packets obtained from a virtual interface to the peer. -func (device *Device) RoutineSequentialSender() { - for { - buf := make([]byte, 1500) - size, err := device.Tun.Device.Read(buf) - if err != nil { - logger.LogErr("Failed to receive virtual IP packet", "error", err) - } - - if size == 0 { - logger.LogErr("Received packet is too small", "error", size) - continue - } - - switch buf[0] >> 4 { - case ipv4.Version: - if len(buf) < ipv4.HeaderLen { - logger.LogErr("Received IPv4 packet is too small", "error", len(buf)) - buf = nil - continue - } - // fmt.Println("[DEBUG] Received IPv4 packet from TUN/TAP", buf[:size]) - // dst := buf[layers.IPv4offsetDst : layers.IPv4offsetDst+net.IPv4len] - - if _, err := device.Peer.ConnUDP.WriteToUDP(buf, &device.Peer.PeerEndPoint); err != nil { - logger.LogErr("Failed to write to real interface", "error", err) - } - - case ipv6.Version: - if len(buf) < ipv6.HeaderLen { - logger.LogErr("Received IPv6 packet is too small", "error", len(buf)) - buf = nil - continue - } - // fmt.Println("[DEBUG] Received IPv6 packet from TUN/TAP", buf[:size]) - // dst := buf[layers.IPv6offsetDst : layers.IPv6offsetDst+net.IPv6len] - - if _, err := device.Peer.ConnUDP.WriteToUDP(buf, &device.Peer.PeerEndPoint); err != nil { - logger.LogErr("Failed to write to real interface", "error", err) - } - - default: - fmt.Println("ip version error") - os.Exit(1) - } - } -} diff --git a/server/chorus/listen.go b/server/internal/server.go similarity index 60% rename from server/chorus/listen.go rename to server/internal/server.go index 0d5c528..c07245d 100644 --- a/server/chorus/listen.go +++ b/server/internal/server.go @@ -1,4 +1,4 @@ -package chorus +package internal import ( "net" @@ -6,13 +6,13 @@ import ( "github.com/GotoRen/echoman/server/internal/logger" ) -func portConf(li string, lp int) (*net.UDPConn, error) { +func portConf(lp uint16) (*net.UDPConn, error) { udpAddr := &net.UDPAddr{ - IP: net.ParseIP(li), - Port: lp, + IP: net.IPv4zero.To4(), + Port: int(lp), } - c, err := net.ListenUDP("udp", udpAddr) + c, err := net.ListenUDP("udp4", udpAddr) if err != nil { return nil, err } @@ -27,8 +27,8 @@ func listenUDPPort(c *net.UDPConn) { } } -func Listen(appIP string, appPort int) { - conn, err := portConf(appIP, appPort) +func (device *Device) ListenServer() { + conn, err := portConf(device.LocalUDPPort) if err != nil { logger.LogErr("Failed to create connection", "error", err) } diff --git a/server/internal/tun.go b/server/internal/tun.go deleted file mode 100644 index 2137cbe..0000000 --- a/server/internal/tun.go +++ /dev/null @@ -1,152 +0,0 @@ -package internal - -import ( - "fmt" - "os/exec" - "runtime" - - "github.com/GotoRen/echoman/server/internal/logger" - "github.com/songgao/water" - "github.com/vishvananda/netlink" - "golang.org/x/xerrors" -) - -// TunInterface manages Tunnel device. -type TunInterface struct { - Tun *water.Interface - address string -} - -// NewTunInterface returns Tunnel device. -func NewTunInterface(name string, address string, prefix string) (*TunInterface, error) { - addr := address + prefix - - switch runtime.GOOS { - case "linux": - config := water.Config{ - DeviceType: water.TUN, - } - config.Name = name - - ifce, err := water.New(config) - if err != nil { - return nil, err - } - - iface := &TunInterface{ - Tun: ifce, - address: addr, - } - - return iface, nil - - case "darwin": - ifce, err := water.New(water.Config{ - DeviceType: water.TUN, - }) - if err != nil { - return nil, err - } - - iface := &TunInterface{ - Tun: ifce, - address: addr, - } - - return iface, nil - - case "windows": - return nil, xerrors.Errorf("Windows is not supported") - default: - return nil, xerrors.Errorf("%s is not supported", runtime.GOOS) - } -} - -// Up function ups a virtual interface. -func (iface *TunInterface) Up() error { - switch runtime.GOOS { - case "linux": - out, err := execCmd("ip", []string{"addr", "add", iface.address, "dev", iface.Tun.Name()}) - logger.LogDebug("Add a Virtual Interface", "Virtual Interface", out) - - if err != nil { - logger.LogErr("ip command add fail", "error", err) - return err - } - - set, err := execCmd("ip", []string{"link", "set", "dev", iface.Tun.Name(), "up", "mtu", "1460"}) - logger.LogDebug("Up a Virtual Interface", "Virtual Interface", set) - - if err != nil { - logger.LogErr("ip command set fail", "error", err) - return err - } - - case "darwin": - out, err := execCmd("ifconfig", []string{iface.Tun.Name(), "up"}) - logger.LogDebug("Add a Virtual Interface", "Virtual Interface", out) - - if err != nil { - logger.LogErr("ifconfig fail", "error", err) - return err - } - - if tun, err := netlink.LinkByName(iface.Tun.Name()); err == nil { - addr, err := netlink.ParseAddr(iface.address) - if err != nil { - logger.LogErr("Unable to parse address", "error", err) - } - - if err := netlink.AddrAdd(tun, addr); err != nil { - logger.LogErr("Unable to add IP address to linked device", "error", err) - } - - // TODO: Change MTU - - logger.LogDebug("Check Virtual Interface Name", "Virtual Interface Name", iface.Tun.Name()) - logger.LogDebug("Check Virtual Interface Address", "Virtual Interface Address", iface.address) - } - - default: - logger.LogErr("unsupported", "error", runtime.GOOS) - logger.LogErr("unsupported", "error", runtime.GOARCH) - return fmt.Errorf("unsupported: %s %s", runtime.GOOS, runtime.GOARCH) - } - - return nil -} - -// Read function read the virtual interface. -func (iface *TunInterface) Read(buf []byte) (int, error) { - n, err := iface.Tun.Read(buf) - // Read Virtual Interface. - if err != nil { - logger.LogErr("Failed to read virtual interface", "error", err) - return 0, err - } - - return n, nil -} - -// Write function write the virtual interface. -func (iface *TunInterface) Write(buf []byte) (int, error) { - return iface.Tun.Write(buf) -} - -// Close function closes the virtual interface. -func (iface *TunInterface) Close() { - if err := iface.Tun.Close(); err != nil { - logger.LogErr("Failed to close virtual interface", "error", err) - } -} - -// execCmd executes the given arguments as a command. -func execCmd(cmd string, args []string) (string, error) { - execCmd := exec.Command(cmd, args...) - if err := execCmd.Run(); err != nil { - logger.LogErr("Unable to execute command ", "error", err.Error()) - return execCmd.String(), err - } - - return execCmd.String(), nil -} diff --git a/server/internal/vip.go b/server/internal/vip.go deleted file mode 100644 index 5faee69..0000000 --- a/server/internal/vip.go +++ /dev/null @@ -1,86 +0,0 @@ -package internal - -import ( - "crypto/rand" - "encoding/binary" - "fmt" - "net" - "strings" - - "github.com/GotoRen/echoman/server/internal/logger" - "github.com/google/uuid" -) - -// rfc3330, rfc2544. -// Using the Documentation addresses and prefixes. -var VIPv4NetworkAddress string = "198.18.0.0/16" -var VIPv6NetworkAddress string = "2001:0db8:c0ff:ee00::/64" - -// getVIPv4NetworkAddressInfo returns information about virtual IPv4 overlay network. -func getVIPv4NetworkAddressInfo() (vipv4nwaddress net.IP, vipv4netmask net.IPMask, vipv4prefix string) { - _, ipnet, err := net.ParseCIDR(VIPv4NetworkAddress) - if err != nil { - logger.LogErr("Unable to get Virtual IPv4 network address", "error", err) - } - - vipv4nwaddress = ipnet.IP - vipv4netmask = ipnet.Mask - vipv4prefix = VIPv4NetworkAddress[strings.Index(VIPv4NetworkAddress, "/"):] - - return -} - -// getVIPv6NetworkAddressInfo returns information about virtual IPv6 overlay network. -func getVIPv6NetworkAddressInfo() (vipv6nwaddress net.IP, vipv6netmask net.IPMask, vipv6prefix string) { - _, ipnet, err := net.ParseCIDR(VIPv6NetworkAddress) - if err != nil { - logger.LogErr("Unable to get Virtual IPv4 network address", "error", err) - } - - vipv6nwaddress = ipnet.IP - vipv6netmask = ipnet.Mask - vipv6prefix = VIPv6NetworkAddress[strings.Index(VIPv6NetworkAddress, "/"):] - - return -} - -// generateVirtualIPv4 is to generate virtual IPv4 address. -func generateVirtualIPv4() (net.IP, string) { - vipv4nwaddress, _, vipv4prefix := getVIPv4NetworkAddressInfo() - b := make([]byte, 2) - -createVIPv4: - err := binary.Read(rand.Reader, binary.BigEndian, &b) - if err != nil { - logger.LogErr("Failed to generate random 2 bytes for VirtualIPv4", "error", err) - } - - vipv4 := net.IPv4(vipv4nwaddress[0], vipv4nwaddress[1], b[0], b[1]).To4() - - // TODO: If you use netip.Addr type you can use switch-case. - // 198.18.0.0/16 => network address or 198.18.255.255/16 => broadcast address - if vipv4.Equal(net.IPv4(vipv4nwaddress[0], vipv4nwaddress[1], 0x00, 0x00).To4()) || vipv4.Equal(net.IPv4(vipv4nwaddress[0], vipv4nwaddress[1], 0xff, 0xff).To4()) { - goto createVIPv4 - } - - return vipv4, vipv4prefix -} - -// generateVirtualIPv6 is to generate virtual IPv6 address. -func generateVirtualIPv6() (net.IP, string) { - vipv6nwaddress, _, vipv6prefix := getVIPv6NetworkAddressInfo() - - u, err := uuid.NewRandom() - if err != nil { - logger.LogErr("Failed to generate UUID for VirtualIPv6", "error", err) - } - - ub, err := u.MarshalBinary() - if err != nil { - logger.LogErr("For VirtualIPv6: Failed encode to binary format", "error", err) - } - - vipv6 := net.ParseIP(strings.Replace(vipv6nwaddress.String(), "::", fmt.Sprintf(":%x:%x:%x:%x", ub[8:10], ub[10:12], ub[12:14], ub[14:16]), 1)) - - return vipv6, vipv6prefix -} diff --git a/server/layers/icmpv4.go b/server/layers/icmpv4.go index 092a20f..c0f450e 100644 --- a/server/layers/icmpv4.go +++ b/server/layers/icmpv4.go @@ -39,7 +39,7 @@ const ( ) func UnmarshalICMPv4Packet(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) icmpv4Layer := packet.Layer(golayers.LayerTypeICMPv4) if icmpv4Layer != nil { icmpv4, _ := icmpv4Layer.(*golayers.ICMPv4) @@ -54,7 +54,7 @@ func UnmarshalICMPv4Packet(b []byte) { } func DebugICMPv4Message(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) icmpv4Layer := packet.Layer(golayers.LayerTypeICMPv4) if icmpv4Layer != nil { icmpv4, _ := icmpv4Layer.(*golayers.ICMPv4) diff --git a/server/layers/ipv4.go b/server/layers/ipv4.go index f85ed4a..c77147b 100644 --- a/server/layers/ipv4.go +++ b/server/layers/ipv4.go @@ -2,7 +2,6 @@ package layers import ( "fmt" - "net" "github.com/google/gopacket" golayers "github.com/google/gopacket/layers" @@ -41,15 +40,13 @@ const ( DstIPv4Length = 4 ) -// IPv4 offset length. const ( - IPv4offsetTotalLength = 2 // IPv4offsetPayloadLength is IPv4 offset payload length. - IPv4offsetSrc = 12 // IPv4offsetSrc is IPv4 offset src length. - IPv4offsetDst = IPv4offsetSrc + net.IPv4len // IPv4offsetDst is IPv4 offset dst length. + SrcIPv4AddrOffset = 26 + DstIPv4AddrOffset = 30 ) func UnmarshalIPv4Packet(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) ipv4Layer := packet.Layer(golayers.LayerTypeIPv4) if ipv4Layer != nil { ipv4, _ := ipv4Layer.(*golayers.IPv4) diff --git a/server/layers/ipv6.go b/server/layers/ipv6.go deleted file mode 100644 index eab3021..0000000 --- a/server/layers/ipv6.go +++ /dev/null @@ -1,10 +0,0 @@ -package layers - -import "net" - -// IPv6 offset length. -const ( - IPv6offsetPayloadLength = 4 // IPv6offsetPayloadLength is IPv6 offset payload length. - IPv6offsetSrc = 8 // IPv6offsetSrc is IPv6 offset src length. - IPv6offsetDst = IPv6offsetSrc + net.IPv6len // IPv6offsetDst is IPv6 offset dst length. -) diff --git a/server/layers/udp.go b/server/layers/udp.go index ace08bf..bb68f73 100644 --- a/server/layers/udp.go +++ b/server/layers/udp.go @@ -13,12 +13,12 @@ const ( ) const ( - SrcUDPPortOffset = 20 - DstUDPPortOffset = 22 + SrcUDPPortOffset = 34 + DstUDPPortOffset = 36 ) func UnmarshalUDPPacket(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) udpLayer := packet.Layer(golayers.LayerTypeUDP) if udpLayer != nil { udp, _ := udpLayer.(*golayers.UDP) @@ -34,7 +34,7 @@ func UnmarshalUDPPacket(b []byte) { } func DebugUDPMessage(b []byte) { - packet := gopacket.NewPacket(b, golayers.LayerTypeIPv4, gopacket.Default) + packet := gopacket.NewPacket(b, golayers.LayerTypeEthernet, gopacket.Default) udpLayer := packet.Layer(golayers.LayerTypeUDP) if udpLayer != nil { udp, _ := udpLayer.(*golayers.UDP)