Files
netstack/tcpip/transport/udp/endpoint.go
impact-eintr 8d8c8fca1d 开坑UDP
2022-11-29 20:20:34 +08:00

282 lines
7.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package udp
import (
"log"
"netstack/tcpip"
"netstack/tcpip/buffer"
"netstack/tcpip/header"
"netstack/tcpip/stack"
"netstack/waiter"
"sync"
)
// udp报文结构 当收到udp报文时 会用这个结构来保存udp报文数据
type udpPacket struct {
udpPacketEntry // 链表实现
// TODO 需要添加
}
type endpointState int
// 表示UDP端的状态参数
const (
stateInitial endpointState = iota
stateBound
stateConnected
stateClosed
)
type endpoint struct {
stack *stack.Stack // udp所依赖的用户协议栈
netProto tcpip.NetworkProtocolNumber // udp网络协议号 ipv4/ipv6
waiterQueue *waiter.Queue // TODO 需要解析
// TODO 需要解析
// The following fields are used to manage the receive queue, and are
// protected by rcvMu.
rcvMu sync.Mutex
rcvReady bool
rcvList udpPacketList
rcvBufSizeMax int
rcvBufSize int
rcvClosed bool
rcvTimestamp bool
// The following fields are protected by the mu mutex.
mu sync.RWMutex
sndBufSize int // 发送缓冲区大小
id stack.TransportEndpointID
state endpointState
bindNICID tcpip.NICID // 绑定的网卡
regNICID tcpip.NICID //
route stack.Route // 路由? TODO
dstPort uint16 // 目标端口
v6only bool // 仅支持ipv6
multicastTTL uint8 // 广播TTL
// shutdownFlags represent the current shutdown state of the endpoint.
shutdownFlags tcpip.ShutdownFlags
// TODO
// effectiveNetProtos contains the network protocols actually in use. In
// most cases it will only contain "netProto", but in cases like IPv6
// endpoints with v6only set to false, this could include multiple
// protocols (e.g., IPv6 and IPv4) or a single different protocol (e.g.,
// IPv4 when IPv6 endpoint is bound or connected to an IPv4 mapped
// address). 当前生效的网络层协议
effectiveNetProtos []tcpip.NetworkProtocolNumber
}
func newEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber,
waiterQueue *waiter.Queue) *endpoint {
log.Println("新建一个udp端")
return &endpoint{
stack: stack,
netProto: netProto,
waiterQueue: waiterQueue,
multicastTTL: 1,
rcvBufSizeMax: 32 * 1024,
sndBufSize: 32 * 1024}
}
// Close UDP端的关闭释放相应的资源
func (e *endpoint) Close() {
e.mu.Lock()
e.shutdownFlags = tcpip.ShutdownRead | tcpip.ShutdownWrite
switch e.state {
case stateBound, stateConnected:
// 释放在协议栈中注册的UDP端
e.stack.UnregisterTransportEndpoint(e.regNICID, e.effectiveNetProtos, ProtocolNumber, e.id)
// 释放端口占用
e.stack.ReleasePort(e.effectiveNetProtos, ProtocolNumber, e.id.LocalAddress, e.id.LocalPort)
}
// TODO
e.mu.Unlock()
}
func (e *endpoint) Read(*tcpip.FullAddress) (buffer.View, tcpip.ControlMessages, *tcpip.Error) {
return nil, tcpip.ControlMessages{}, nil
}
func (e *endpoint) Write(tcpip.Payload, tcpip.WriteOptions) (uintptr, <-chan struct{}, *tcpip.Error) {
return 0, nil, nil
}
func (e *endpoint) Peek([][]byte) (uintptr, tcpip.ControlMessages, *tcpip.Error) {
return 0, tcpip.ControlMessages{}, nil
}
// IPV6于IPV4地址的映射
func (e *endpoint) checkV4Mapped(addr *tcpip.FullAddress, allowMismatch bool) (tcpip.NetworkProtocolNumber, *tcpip.Error) {
netProto := e.netProto
if header.IsV4MappedAddress(addr.Addr) {
// Fail if using a v4 mapped address on a v6only endpoint.
if e.v6only {
return 0, tcpip.ErrNoRoute
}
netProto = header.IPv4ProtocolNumber
addr.Addr = addr.Addr[header.IPv6AddressSize-header.IPv4AddressSize:]
if addr.Addr == "\x00\x00\x00\x00" {
addr.Addr = ""
}
// Fail if we are bound to an IPv6 address.
if !allowMismatch && len(e.id.LocalAddress) == 16 {
return 0, tcpip.ErrNetworkUnreachable
}
}
// Fail if we're bound to an address length different from the one we're
// checking.
// 源地址用与目标地址使用的ip协议不能不一致
if l := len(e.id.LocalAddress); l != 0 && l != len(addr.Addr) {
return 0, tcpip.ErrInvalidEndpointState
}
return netProto, nil
}
func (e *endpoint) Connect(address tcpip.FullAddress) *tcpip.Error {
log.Println("连接")
return nil
}
func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
return nil
}
func (e *endpoint) Listen(backlog int) *tcpip.Error {
return nil
}
func (e *endpoint) Accept() (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
return nil, nil, nil
}
func (e *endpoint) registerWithStack(nicid tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber,
id stack.TransportEndpointID) (stack.TransportEndpointID, *tcpip.Error) {
if e.id.LocalPort == 0 { // 一个没有绑定过端口的udp端
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort) // 为这个udp端绑定一个端口
if err != nil {
return id, err
}
id.LocalPort = port
}
err := e.stack.RegisterTransportEndpoint(nicid, netProtos, ProtocolNumber, id, e)
if err != nil {
// 释放端口
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort)
}
return id, err
}
func (e *endpoint) bindLocked(addr tcpip.FullAddress, commit func() *tcpip.Error) *tcpip.Error {
// 不是初始状态的UDP实现不允许绑定
if e.state != stateInitial {
return tcpip.ErrInvalidEndpointState
}
netProto, err := e.checkV4Mapped(&addr, true)
if err != nil {
return nil
}
netProtos := []tcpip.NetworkProtocolNumber{netProto}
if netProto == header.IPv6ProtocolNumber && !e.v6only && addr.Addr == "" { // IPv6 && 支持ipv4 & 任意地址
netProtos = []tcpip.NetworkProtocolNumber{
header.IPv6ProtocolNumber,
header.IPv4ProtocolNumber,
}
}
// 不是任意地址的话 需要检验本地网卡是否绑定个这个ip地址
if len(addr.Addr) != 0 {
if e.stack.CheckLocalAddress(addr.NIC, netProto, addr.Addr) == 0 {
return tcpip.ErrBadLocalAddress
}
}
// 开始绑定 绑定的时候 传输端ID : srcIP + srcPort
id := stack.TransportEndpointID{
LocalAddress: addr.Addr,
LocalPort: addr.Port,
}
// 在协议栈中注册该UDP端
id, err = e.registerWithStack(addr.NIC, netProtos, id)
if err != nil {
return err
}
// 如果指定了 commit 函数 执行并处理错误
if commit != nil {
if err := commit(); err != nil {
// Unregister, the commit failed.
e.stack.UnregisterTransportEndpoint(addr.NIC, netProtos, ProtocolNumber, id)
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort)
return err
}
}
e.id = id
e.regNICID = addr.NIC
e.effectiveNetProtos = netProtos
// Mark endpoint as bound.
// 标记状态为已绑定
e.state = stateBound
return nil
}
// Bind binds the endpoint to a specific local address and port.
// Specifying a NIC is optional.
// Bind 将该UDP端绑定本地的一个IP+端口
// 例如绑定本地0.0.0.0的9000端口那么其他机器给这台机器9000端口发消息该UDP端就能收到消息了
func (e *endpoint) Bind(addr tcpip.FullAddress, commit func() *tcpip.Error) *tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
// 执行绑定IP+端口操作
err := e.bindLocked(addr, commit)
if err != nil {
return err
}
// 绑定的网卡ID
e.bindNICID = addr.NIC
return nil
}
func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
return tcpip.FullAddress{}, nil
}
func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) {
return tcpip.FullAddress{}, nil
}
func (e *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
return waiter.EventErr
}
func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
return nil
}
func (e *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
return nil
}
// 从网络层接收到UDP数据报时的处理函数
func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv buffer.VectorisedView) {
}
// HandleControlPacket implements stack.TransportEndpoint.HandleControlPacket.
func (e *endpoint) HandleControlPacket(id stack.TransportEndpointID, typ stack.ControlType, extra uint32, vv buffer.VectorisedView) {
}