network

package
v0.0.0-...-d0e9c6f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 30, 2024 License: MIT Imports: 24 Imported by: 0

README

reference

mosh network connection

Connection()

  • Connection() calls key() to generate a random key for the connection. key() is of type Base64Key.
  • Connection() calls session() with the above key as parameter. session() is in charge of encrypt/decrypt.
  • Connection() calls setup() to update the last_port_choice field with current time.
  • Connection() calls try_bind() to bing to desired IP first, if we have desired_ip parameter; try_bind() return on success.
  • Connection() calls try_bind() to try any local interface, return on sucess. see next section for detail.
try_bind()
  • try_bind() builds an instance for AddrInfo class with the following addrinfo as hint:
    • getaddrinfo() is called in the constructor of AddrInfo class to create struct addrinfo.
    • hints.ai_family = AF_UNSPEC;
    • hints.ai_socktype = SOCK_DGRAM;
    • hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;
    • The Linux programming interface Page 1213~1216
  • try_bind() builds an instance for Socket class with the local address family as parameter.
    • Socket() calls socket() to create socket with the following options.
      • SOCK_DGRAM is the main parameter for the socket() system call.
      • Advanced programming in the UNIX Environment Page 600
    • Socket() calls setsockopt() to set socket options several times for different environment, such as:
      • IPPROTO_IP, IP_MTU_DISCOVER
      • IPPROTO_IP, IP_TOS
      • IPPROTO_IP, IP_RECVTOS
      • UNIX Network Programming: The Sockets Networking API Page 193,214
  • try_bind() iterates from the low port to the high port number, for each port:
    • try_bind() calls bind() the socket to the address.
    • UNIX Network Programming: The Sockets Networking API Page 101,102
    • try_bind() calls set_MTU() to set MTU.
    • upon the socket is bind to the address and port, return.
    • otherwise, increase the port number, go through the next iteration.
  • if the high port number is reached, try_bind() prints the error message and quits with error message.

mosh client roaming

receive a packet from client

  • recv() receives a packet from remote.
  • recv() iterates through each connection saved in Connection.
  • recv() calls recv_one() for each connection to get the payload.
    • recv_one() a.k.a. Connection:recv_one().
    • recv_one() receives a packet from client, the server checks the cached remote_addr field against the packet_remote_addr value.
    • recv_one() updates the remote_addr and remote_addr_len fields if it's different from the previous packet.
  • recv() calls prune_sockets() to clean old socket.

send packet to remote

  • send() sends a packet to remote.
  • send() creates a Packet based on the payload.
  • send() encrypts the Packet message.
  • send() calls sendto() to send the packet to the remote with the last socket in socket list.
  • send() check the sent data size to check the error.
  • for server side:
    • send() checks the last_heard time, if no contact since SERVER_ASSOCIATION_TIMEOUT, set has_remote_addr false.
  • for client side:
    • send() checks the last_port_choice and last_roundtrip_success, if PORT_HOP_INTERVAL passed, calls hop_port()

hop port

  • hop_port() a.k.a. Connection.hop_port().
  • hop_port() calls setup() to update last_port_choice.
  • hop_port() creates a new socket to the remote_addr and add it to the socket list in Connection.
  • hop_port() calls prune_sockets() to clean old socket.

client main loop

output frame
  • pick the latest remote state.
  • add state to overlay.
  • calculate the state difference and send it to local output.
  • update the latest remote state.
process network input

In case there is network input:

  • receive the data packet from server.
  • update notification engine.
  • update prediction engine.
process user input

In case there is user input:

  • read user input (local).
  • process shutdown if ready.
  • update prediction engine.
  • save user key stroke into the currrent local state.
process shutdown
  • all kinds of cleanup.
send data to server
  • calculate the state difference for sending
  • create Instruction instance.
  • put the state difference into Instruction instance.
  • save the sent state
  • send the Instruction in fragments if possible.

server main loop

calculate the server timeout
  • go asleep if there is no remote peer
receive data packet from client
  • receive the data packet from client
  • apply userstream to terminal
  • save current terminal state for sync to client
receive output from local
  • read data from local standard output.
  • apply terminal instruction to local terminal.
  • set local current state.
process shutdown
  • all kinds of cleanup
send data to client
  • calculate the state difference for sending
  • create Instruction instance.
  • put the state difference into Instruction instance.
  • save the sent state
  • send the Instruction in fragments if possible.

go net package

ListenConfig.ListenPacket()

I choose ListenConfig.ListenPacket() because ListenConfig allow us to change socket configuration and parameters. ListenConfig.ListenPacket() calls sl.listenUDP() to get the listening socket.

  • sl.listenUDP() a.k.a. sysListener.listenUDP().
  • sysListener.listenUDP() calls internetSocket() to get the socket file descriptor.
  • sysListener.listenUDP() calls internetSocket() with SOCK_DGRAM as socket type parameter.
    • internetSocket() calls favoriteAddrFamily() to get the address family and ipv6only variable.
    • internetSocket() calls socket() to create the socket file descriptor. see next section for detail.
  • sysListener.listenUDP() calls newUDPConn() to build a UDPConn type value.

socket()

  • socket() calls sysSocket to create the socket.
    • sysSocket() calls system call socket() and set SOCK_NONBLOCK and SOCK_CLOEXEC option for the socket.
  • socket() calls setDefaultSockopts to set socket options.
    • setDefaultSockopts() calls system call setsockopt() and set SOL_SOCKET and SO_BROADCAST.
  • socket() calls fd.listenDatagram() for SOCK_DGRAM when local addr is not nil but remote addr is nil.
  • socket() calls fd.listenStream() for SOCK_STREAM when local addr is not nil but remote addr is nil. see next section for detail.
  • socket() calls fd.dial() to initialize dialer socket file descriptor. see next section for detail.

listenStream()

  • fd.listenStream() a.k.a. netFD.listenDatagram().
  • listenStream() calls laddr.sockaddr() to convert local address to syscall.Sockaddr type.
  • listenStream() calls ctrlFn defined in ListenConfig to setup the socket options.
  • listenStream() calls system call bind() to bind the socket to the local address.
  • listenStream() calls fd.init() to initialize the socket file descriptor for netpoll
  • listenStream() calls syscall.Getsockname() to get the socket name.
  • listenStream() calls fd.addrFunc() to convert the local address from syscall.Sockaddr type to UDPAddr type.
  • listenStream() returns the socket file descriptor.

dial()

  • fd.dial() a.k.a. netFD.dial().
  • dial() calls ctrlFn defined in ListenConfig to setup the socket options.
  • if there is local address:
    • dial() calls laddr.sockaddr() to convert local address to syscall.Sockaddr type.
    • dial() calls system call bind() to bind the socket to the local address.
  • if there is remote address:
    • dial() calls raddr.sockaddr() to convert remote address to syscall.Sockaddr type.
    • dial() calls system call connect() to connect to the remote address and initialize the socket file descriptor for netpoll.
  • dial() calls syscall.Getsockname() to get the socket name.
  • dial() calls fd.setAddr() method to set the local and remote address field in netFD.

Documentation

Index

Constants

View Source
const (
	DIRECTION_MASK uint64 = uint64(1) << 63
	SEQUENCE_MASK  uint64 = ^DIRECTION_MASK
)
View Source
const (
	/*
	 * For IPv4, guess the typical (minimum) header length;
	 * fragmentation is not dangerous, just inefficient.
	 */
	IPV4_HEADER_LEN = 20 + 8 // base IP header + UDP

	/*
	 * For IPv6, we don't want to ever have MTU issues, so make a
	 * conservative guess about header size.
	 */
	IPV6_HEADER_LEN = 40 + 16 + 8 // base IPv6 header + 2 minimum-sized extension headers + UDP */

	/* Application datagram MTU. For constructors and fallback. */
	DEFAULT_SEND_MTU = 500

	/*
	 * IPv4 MTU. Don't use full Ethernet-derived MTU,
	 * mobile networks have high tunneling overhead.
	 *
	 * As of July 2016, VPN traffic over Amtrak Acela wifi seems to be
	 * dropped if tunnelled packets are 1320 bytes or larger.  Use a
	 * 1280-byte IPv4 MTU for now.
	 *
	 * We may have to implement ICMP-less PMTUD (RFC 4821) eventually.
	 */
	DEFAULT_IPV4_MTU = 1280

	/* IPv6 MTU. Use the guaranteed minimum to avoid fragmentation. */
	DEFAULT_IPV6_MTU = 1280

	MIN_RTO int64 = 50   // ms
	MAX_RTO int64 = 1000 // ms

	PORT_RANGE_LOW  = 60001
	PORT_RANGE_HIGH = 60999

	SERVER_ASSOCIATION_TIMEOUT = 40000
	PORT_HOP_INTERVAL          = 10000

	MAX_PORTS_OPEN     = 10
	MAX_OLD_SOCKET_AGE = 60000

	CONGESTION_TIMESTAMP_PENALTY = 500 // ms

	NETWORK = "udp" // IPv6 is only supported on Docker daemons running on Linux hosts.

	// Network transport overhead.
	ADDED_BYTES = 8 + 4 /* timestamps */
)
View Source
const (
	SEND_INTERVAL_MIN    = 20    /* ms between frames */
	SEND_INTERVAL_MAX    = 250   /* ms between frames */
	ACK_INTERVAL         = 3000  /* ms between empty acks */
	ACK_DELAY            = 100   /* ms before delayed ack */
	SHUTDOWN_RETRIES     = 16    /* number of shutdown packets to send before giving up */
	ACTIVE_RETRY_TIMEOUT = 10000 /* attempt to resend at frame rate */
)

Variables

View Source
var (
	ErrRecvLength    = errors.New("#recvOne receive zero or negative length data.")
	ErrRecvOversize  = errors.New("#recvOne received oversize datagram.")
	ErrRecvDirection = errors.New("#recvOne direction is wrong.")
)

Functions

func ParsePortRange

func ParsePortRange(desiredPort string) (desiredPortLow, desiredPortHigh int, ok bool)

parse "port" or "portlow:porthigh"

Types

type Compressor

type Compressor struct {
	// contains filtered or unexported fields
}

func GetCompressor

func GetCompressor() *Compressor

func (*Compressor) Compress

func (c *Compressor) Compress(input []byte) ([]byte, error)

func (*Compressor) Uncompress

func (c *Compressor) Uncompress(input []byte) ([]byte, error)

type Connection

type Connection struct {
	RTTHit bool
	SRTT   float64 // smoothed round-trip time
	RTTVAR float64 // round-trip time variation

	sync.RWMutex
	// contains filtered or unexported fields
}

func NewConnection

func NewConnection(desiredIp string, desiredPort string) *Connection

func NewConnectionClient

func NewConnectionClient(keyStr string, ip, port string) *Connection

func (*Connection) Close

func (c *Connection) Close()

func (*Connection) Recv

func (c *Connection) Recv(timeout int) (payload string, remoteAddr net.Addr, err error)

receive packet from remote side, for client, there might be sevral connections to the server, Recv() will iterate every connection in order and read from the connection with the specified timeout (millisecond) value.

type Direction

type Direction uint
const (
	APRILSH_PROTOCOL_VERSION = 3 // bumped for echo-ack

	TO_SERVER Direction = iota
	TO_CLIENT
)

type Fragment

type Fragment struct {
	// contains filtered or unexported fields
}

func NewFragment

func NewFragment(id uint64, fragmentNum uint16, final bool, contents string) *Fragment

func NewFragmentFrom

func NewFragmentFrom(x string) *Fragment

convert network order string into Fragment

func (*Fragment) String

func (f *Fragment) String() string

convert Fragment into network order string

type FragmentAssembly

type FragmentAssembly struct {
	// contains filtered or unexported fields
}

func NewFragmentAssembly

func NewFragmentAssembly() *FragmentAssembly

type Fragmenter

type Fragmenter struct {
	// contains filtered or unexported fields
}

func NewFragmenter

func NewFragmenter() *Fragmenter

type Packet

type Packet struct {
	// contains filtered or unexported fields
}

Packet is used for RTT calculation

func NewPacket

func NewPacket(direction Direction, timestamp uint16, timestampReply uint16, payload []byte) *Packet

func NewPacketFrom

func NewPacketFrom(m *encrypt.Message) *Packet

type State

type State[C any] interface {
	// interface for Network::Transport
	Subtract(x C)
	DiffFrom(x C) string
	InitDiff() string
	ApplyString(diff string) error
	Equal(x C) bool
	EqualTrace(x C) bool // for test purpose

	// interface from code
	ResetInput()
	Reset()
	InitSize(y, x int)
	Clone() C
}

State is implemented by UserSteam or CompleteTerminal. The type parameter is required to meet the requirement: the concrete type, such as UserSteam or CompleteTerminal, can use the concrete type for method parameter or return type instead of interface. self reference in method parameter and return type is not common, pay attention to it. [ref](https://appliedgo.com/blog/generic-interface-functions) The meaning of [C any]: the following methods requires a quite unspecified type C - basically, it can be anything.

type TimestampedState

type TimestampedState[T State[T]] struct {
	// contains filtered or unexported fields
}

func (*TimestampedState[T]) GetState

func (t *TimestampedState[T]) GetState() T

func (*TimestampedState[T]) GetTimestamp

func (t *TimestampedState[T]) GetTimestamp() int64

type Transport

type Transport[S State[S], R State[R]] struct {
	// contains filtered or unexported fields
}

type S or R that must implement the State[T] interface - that is, for itself.

func NewTransportClient

func NewTransportClient[S State[S], R State[R]](initialState S, initialRemote R,
	keyStr, ip, port string) *Transport[S, R]

func NewTransportServer

func NewTransportServer[S State[S], R State[R]](initialState S, initialRemote R,
	desiredIp, desiredPort string) *Transport[S, R]

func (*Transport[S, R]) Awaken

func (t *Transport[S, R]) Awaken(now int64) (ret bool)

detect computer awaken from hibernate based on receivedState and sentStates.

func (*Transport[S, R]) Close

func (t *Transport[S, R]) Close()

func (*Transport[S, R]) CounterpartyShutdownAckSent

func (t *Transport[S, R]) CounterpartyShutdownAckSent() bool

Other side has requested shutdown and we have sent one ACK

func (*Transport[S, R]) GetConnection

func (t *Transport[S, R]) GetConnection() *Connection

func (*Transport[S, R]) GetCurrentState

func (t *Transport[S, R]) GetCurrentState() S

func (*Transport[S, R]) GetKey

func (t *Transport[S, R]) GetKey() string

func (*Transport[S, R]) GetLatestRemoteState

func (t *Transport[S, R]) GetLatestRemoteState() TimestampedState[R]

func (*Transport[S, R]) GetRemoteAddr

func (t *Transport[S, R]) GetRemoteAddr() net.Addr

func (*Transport[S, R]) GetRemoteDiff

func (t *Transport[S, R]) GetRemoteDiff() string

func (*Transport[S, R]) GetRemoteStateNum

func (t *Transport[S, R]) GetRemoteStateNum() uint64

func (*Transport[S, R]) GetSentStateAcked

func (t *Transport[S, R]) GetSentStateAcked() uint64

func (*Transport[S, R]) GetSentStateAckedTimestamp

func (t *Transport[S, R]) GetSentStateAckedTimestamp() int64

func (*Transport[S, R]) GetSentStateLast

func (t *Transport[S, R]) GetSentStateLast() uint64

func (*Transport[S, R]) GetSentStateLastTimestamp

func (t *Transport[S, R]) GetSentStateLastTimestamp() int64

func (*Transport[S, R]) GetServerPort

func (t *Transport[S, R]) GetServerPort() string

func (*Transport[S, R]) HasRemoteAddr

func (t *Transport[S, R]) HasRemoteAddr() bool

func (*Transport[S, R]) InitSize

func (t *Transport[S, R]) InitSize(nCols, nRows int)

func (*Transport[S, R]) ProcessPayload

func (t *Transport[S, R]) ProcessPayload(s string) error

func (*Transport[S, R]) Recv

func (t *Transport[S, R]) Recv() error

Blocks waiting for a packet.

func (*Transport[S, R]) SentInterval

func (t *Transport[S, R]) SentInterval() uint

func (*Transport[S, R]) SetCurrentState

func (t *Transport[S, R]) SetCurrentState(x S)

func (*Transport[S, R]) SetSendDelay

func (t *Transport[S, R]) SetSendDelay(delay uint)

func (*Transport[S, R]) SetVerbose

func (t *Transport[S, R]) SetVerbose(verbose uint)

func (*Transport[S, R]) ShutdownAckTimedout

func (t *Transport[S, R]) ShutdownAckTimedout() bool

return true if retries reach times limit or retries time out

func (*Transport[S, R]) ShutdownAcknowledged

func (t *Transport[S, R]) ShutdownAcknowledged() bool

return true if the firt sent state num is -1, otherwise false.

func (*Transport[S, R]) ShutdownInProgress

func (t *Transport[S, R]) ShutdownInProgress() bool

return true if shutdown is started, otherwise false.

func (*Transport[S, R]) StartShutdown

func (t *Transport[S, R]) StartShutdown()

Other side has requested shutdown and we have sent one ACK

Illegal to change current_state after this.

func (*Transport[S, R]) Tick

func (t *Transport[S, R]) Tick() error

Send data or an ack if necessary.

func (*Transport[S, R]) WaitTime

func (t *Transport[S, R]) WaitTime() int

Returns the number of ms to wait until next possible event.

type TransportSender

type TransportSender[T State[T]] struct {
	SEND_MINDELAY uint // ms to collect all input
	// contains filtered or unexported fields
}

func NewTransportSender

func NewTransportSender[T State[T]](connection *Connection, initialState T) *TransportSender[T]

func (*TransportSender[T]) Awaken

func (ts *TransportSender[T]) Awaken(now int64) (bool, int)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL