From 8d8c8fca1dfda4e7e6d9287d0845f70cc3ce9a5d Mon Sep 17 00:00:00 2001 From: impact-eintr Date: Tue, 29 Nov 2022 20:20:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=9D=91UDP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/udp/main.go | 167 ++++++++++++++++++++++++++++++++ tcpip/transport/udp/README.md | 18 ++++ tcpip/transport/udp/endpoint.go | 1 + 3 files changed, 186 insertions(+) create mode 100644 cmd/udp/main.go create mode 100644 tcpip/transport/udp/README.md diff --git a/cmd/udp/main.go b/cmd/udp/main.go new file mode 100644 index 0000000..479ccdd --- /dev/null +++ b/cmd/udp/main.go @@ -0,0 +1,167 @@ +package main + +import ( + "flag" + "log" + "net" + "netstack/tcpip" + "netstack/tcpip/link/fdbased" + "netstack/tcpip/link/tuntap" + "netstack/tcpip/network/arp" + "netstack/tcpip/network/ipv4" + "netstack/tcpip/network/ipv6" + "netstack/tcpip/stack" + "netstack/tcpip/transport/udp" + "netstack/waiter" + "os" + "strconv" + "strings" +) + +var mac = flag.String("mac", "01:01:01:01:01:01", "mac address to use in tap device") + +func main() { + flag.Parse() + if len(flag.Args()) != 3 { + log.Fatal("Usage: ", os.Args[0], " port") + } + + log.SetFlags(log.Lshortfile | log.LstdFlags) + tapName := flag.Arg(0) + listeAddr := flag.Arg(1) + portName := flag.Arg(2) + + log.Printf("tap: %v, listeAddr: %v, portName: %v", tapName, listeAddr, portName) + + // Parse the mac address. + maddr, err := net.ParseMAC(*mac) + if err != nil { + log.Fatalf("Bad MAC address: %v", *mac) + } + + parsedAddr := net.ParseIP(listeAddr) + + // 解析地址ip地址,ipv4或者ipv6地址都支持 + var addr tcpip.Address + var proto tcpip.NetworkProtocolNumber + if parsedAddr.To4() != nil { + addr = tcpip.Address(parsedAddr.To4()) + proto = ipv4.ProtocolNumber + } else if parsedAddr.To16() != nil { + addr = tcpip.Address(parsedAddr.To16()) + proto = ipv6.ProtocolNumber + } else { + log.Fatalf("Unknown IP type: %v", parsedAddr) + } + + localPort, err := strconv.Atoi(portName) + if err != nil { + log.Fatalf("Unable to convert port %v: %v", portName, err) + } + + // 虚拟网卡配置 + conf := &tuntap.Config{ + Name: tapName, + Mode: tuntap.TAP, + } + + var fd int + // 新建虚拟网卡 + fd, err = tuntap.NewNetDev(conf) + if err != nil { + log.Fatal(err) + } + + // 启动tap网卡 + _ = tuntap.SetLinkUp(tapName) + // 设置tap网卡IP地址 + _ = tuntap.AddIP(tapName, listeAddr) + + // 抽象网卡的文件接口 + linkID := fdbased.New(&fdbased.Options{ + FD: fd, + MTU: 1500, + Address: tcpip.LinkAddress(maddr), + }) + + // 新建相关协议的协议栈 + s := stack.New([]string{ipv4.ProtocolName, arp.ProtocolName}, + []string{ /*tcp.ProtocolName, */ udp.ProtocolName}, stack.Options{}) + + // 新建抽象的网卡 + if err := s.CreateNamedNIC(1, "vnic1", linkID); err != nil { + log.Fatal(err) + } + + // 在该协议栈上添加和注册相应的网络层 + if err := s.AddAddress(1, proto, addr); err != nil { + log.Fatal(err) + } + + // 在该协议栈上添加和注册ARP协议 + if err := s.AddAddress(1, arp.ProtocolNumber, arp.ProtocolAddress); err != nil { + log.Fatal(err) + } + + // 添加默认路由 + s.SetRouteTable([]tcpip.Route{ + { + Destination: tcpip.Address(strings.Repeat("\x00", len(addr))), + Mask: tcpip.AddressMask(strings.Repeat("\x00", len(addr))), + Gateway: "", + NIC: 1, + }, + }) + + // 同时监听tcp和udp localPort端口 + //tcpEp := tcpListen(s, proto, localPort) + udpEp := udpListen(s, proto, localPort) + // 关闭监听服务,此时会释放端口 + //tcpEp.Close() + udpEp.Close() +} + +//func tcpListen(s *stack.Stack, proto tcpip.NetworkProtocolNumber, localPort int) tcpip.Endpoint { +// var wq waiter.Queue +// // 新建一个tcp端 +// ep, err := s.NewEndpoint(tcp.ProtocolNumber, proto, &wq) +// if err != nil { +// log.Fatal(err) +// } +// +// // 绑定IP和端口,这里的IP地址为空,表示绑定任何IP +// // 此时就会调用端口管理器 +// if err := ep.Bind(tcpip.FullAddress{0, "", uint16(localPort)}, nil); err != nil { +// log.Fatal("Bind failed: ", err) +// } +// +// // 开始监听 +// if err := ep.Listen(10); err != nil { +// log.Fatal("Listen failed: ", err) +// } +// +// return ep +//} + +func udpListen(s *stack.Stack, proto tcpip.NetworkProtocolNumber, localPort int) tcpip.Endpoint { + var wq waiter.Queue + // 新建一个udp端 + ep, err := s.NewEndpoint(udp.ProtocolNumber, proto, &wq) + if err != nil { + log.Fatal(err) + } + + // 绑定IP和端口,这里的IP地址为空,表示绑定任何IP + // 0.0.0.0:9999 这台机器上的所有ip的9999段端口数据都会使用该传输层实现 + // 此时就会调用端口管理器 + if err := ep.Bind(tcpip.FullAddress{NIC: 0, Addr: "", Port: uint16(localPort)}, nil); err != nil { + log.Fatal("Bind failed: ", err) + } + + if err := ep.Connect(tcpip.FullAddress{NIC: 0, Addr: "", Port: uint16(localPort)}); err != nil { + log.Fatal("Conn failed: ", err) + } + + // 注意UDP是无连接的,它不需要Listen + return ep +} diff --git a/tcpip/transport/udp/README.md b/tcpip/transport/udp/README.md new file mode 100644 index 0000000..647088b --- /dev/null +++ b/tcpip/transport/udp/README.md @@ -0,0 +1,18 @@ +# 传输层 + +![img](https://doc.shiyanlou.com/document-uid949121labid10418timestamp1555488741384.png) + +传输层是整个网络体系结构中的关键之一,我们很多编程都是直接和传输层打交道的,我们需要了解以下的概念: +1. 端口的意义 - 上一章已经介绍过了 +2. 无连接 UDP 协议及特点 - 本章介绍 +3. 面向连接 TCP 协议及特点 - 下章会介绍 + +传输层向它上面的应用层提供通信服务,传输题主要提供了以下功能: + +1. 为相互通信的应用进程提供逻辑通信。 网络层是为主机之间提供通信,而传输层是为应用进程之间提供端到端的逻辑通信。 + +2. 复用和分用 复用是指发送方不同的应用进程都可以使用同一个传输协议来传送数据,而分用是指接收方的传输层在剥去报文的首部后, 能够把这些数据正确的交付给目的进程。其实复用和分用就是端口来实现的。 + +3. 报文差错检测 网络层只对 IP 首部进行差错检测,而传输层对整个报文进行差错检测。 + +4. 提供不可靠和可靠通信 网络层只提供了不可靠通信,而在传输层的 TCP 协议提供了可靠通信。 \ No newline at end of file diff --git a/tcpip/transport/udp/endpoint.go b/tcpip/transport/udp/endpoint.go index 9fbaf84..b4b6968 100644 --- a/tcpip/transport/udp/endpoint.go +++ b/tcpip/transport/udp/endpoint.go @@ -142,6 +142,7 @@ func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress, allowMismatch bool) (t } func (e *endpoint) Connect(address tcpip.FullAddress) *tcpip.Error { + log.Println("连接") return nil }