ftcp

package module
v0.0.0-...-36dbe75 Latest Latest
Warning

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

Go to latest
Published: Feb 12, 2023 License: BSD-3-Clause Imports: 16 Imported by: 0

README

ftcp: A user-space, failover, high availability TCP implementation

ftcp is a user-space TCP written in Go, intended to experiment with the idea of a failover-capable stack for high-reliability/availability applications.

ftcp is NOT:

  • A production-quality stack
  • Designed for high performance
  • Spec compliant

There are intentionally non-goals. Rather, the focus is on a straight-forward, easy to understand implementation, which can be used to experiment with some ideas. In particular, synchronous replication of the TCP connection state, to allow failover to a standby when the original endpoint fails (i.e. software crash, hardware failure, someone tripped over the ethernet cable... ooops).

The motivation behind this was dealing with some legacy protocols. These protocols lack retry or session resumption abilities, and tie session state to the TCP connection. If the server end of the connection fails, session recovery may be very difficult or costly (i.e. retrying an FTP upload of a 100G file 99G into the upload. Eek!).

A quick web search reveals some acedemic research into this. In terms of existing software, tcpcp is probably the most well known. Like ftcp, tcpcp requires two servers to be in collusion. Whereas tcpcp is (quoted from the paper):

...primarily designed for scenarios, where the old and the new connection owner are both functional during the process of connection passing.

ftcp is specifically intended for the situation where the old server is non-functional.

Running the code

The code is a hack! Sorry. But if you're so inclined to run it, a simple test server is provided in cmd/test. This runs an echo server on port 9999 (hard coded into the TCP stack). The server's IP address is hard coded and will need to be changed to your server's IP address.

Before running the test server, you'll need to tell iptables to DROP all packets inbound to TCP port 9999, to prevent the kernel's TCP stack from responding to new connections requests (with a RST response, because there is no server running).

sudo iptables -A INPUT -d 192.168.1.134 -p tcp --dport 9999 -j DROP

Replace 192.168.1.134 with your sever's IP address.

The test server needs to run as root, due to the use of raw sockets. Once running, you can use nc from another device to echo a file:

nc -v 192.168.1.134 9999 < random.test > random.received

If the transfer succeeded, the contents of random.received should be idential to random.test

License

This project is provided under a 3-clause BSD license.

Documentation

Index

Constants

View Source
const (
	TcpOptionMss           = 2
	TcpOptionWindowScaling = 3
	TcpOptionSackPermitted = 4
	TcpOptionSack          = 5
	TcpOptionTimestamp     = 8

	TcpMinHeaderSize = 20
)

Variables

View Source
var (
	ErrTimeout = errors.New("sync_client: timeout")
)

Functions

func IPChecksum

func IPChecksum(packet []byte) uint16

func IPSetChecksum

func IPSetChecksum(packet []byte, sum uint16)

func LogDebug

func LogDebug(format string, v ...any)

func LogError

func LogError(format string, v ...any)

func LogFatal

func LogFatal(format string, v ...any)

func LogInfo

func LogInfo(format string, v ...any)

func LogWarn

func LogWarn(format string, v ...any)

func OpenIPSocket

func OpenIPSocket(addr net.IP) (*net.IPConn, error)

func OpenRawSocket

func OpenRawSocket() (io.ReadWriteCloser, error)

func SetLogLevel

func SetLogLevel(l LogLevel)

func TCPChecksum

func TCPChecksum(packet []byte, srcAddr, dstAddr net.IP) uint16

func TCPSetChecksum

func TCPSetChecksum(packet []byte, sum uint16)

Types

type IPConn

type IPConn interface {
	ReadFromIP(b []byte) (int, *net.IPAddr, error)
	WriteToIP(b []byte, addr *net.IPAddr) (int, error)
}

type IPPacket

type IPPacket struct {
	Header *ipv4.Header
	// contains filtered or unexported fields
}

func MakeIPPacket

func MakeIPPacket(buf []byte) (*IPPacket, error)

func (*IPPacket) Payload

func (p *IPPacket) Payload() []byte

func (*IPPacket) String

func (p *IPPacket) String() string

type IPProtcolHandler

type IPProtcolHandler interface {
	HandleIPPacket(packet *IPPacket) error
}

type IPSender

type IPSender interface {
	SendIPPacket(b []byte, addr *net.IPAddr) error
}

type IPStack

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

func NewIPStack

func NewIPStack(conn IPConn, listenAddr *net.IPAddr) *IPStack

func (*IPStack) RegisterProtocolHandler

func (s *IPStack) RegisterProtocolHandler(proto int, handler IPProtcolHandler)

func (*IPStack) Run

func (s *IPStack) Run() error

func (*IPStack) SendIPPacket

func (s *IPStack) SendIPPacket(b []byte, addr *net.IPAddr) error

type LogLevel

type LogLevel int
const (
	LOG_DEBUG LogLevel = iota
	LOG_INFO
	LOG_WARN
	LOG_ERROR
	LOG_FATAL
)

type SyncClient

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

func NewSyncClient

func NewSyncClient(addr string) *SyncClient

func (*SyncClient) Close

func (c *SyncClient) Close() error

func (*SyncClient) SendSync

func (c *SyncClient) SendSync(req *pb.SyncRequest, reply *pb.SyncReply) error

type SyncHandler

type SyncHandler interface {
	Sync(*pb.SyncRequest, *pb.SyncReply) error
}

type SyncServer

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

func NewSyncServer

func NewSyncServer(l net.Listener, h SyncHandler) *SyncServer

type SyncedBuffer

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

func NewSyncedBuffer

func NewSyncedBuffer(initSeq uint32, initWindowSize int) *SyncedBuffer

func (*SyncedBuffer) Ack

func (b *SyncedBuffer) Ack(ack uint32) bool

func (*SyncedBuffer) Append

func (b *SyncedBuffer) Append(buf []byte) int

func (*SyncedBuffer) Cap

func (b *SyncedBuffer) Cap() int

func (*SyncedBuffer) Consume

func (b *SyncedBuffer) Consume(n int)

func (*SyncedBuffer) EndSeq

func (b *SyncedBuffer) EndSeq() uint64

Sequence number of one past the end of the buffer

func (*SyncedBuffer) Fetch

func (b *SyncedBuffer) Fetch(buf []byte, offset int) int

func (*SyncedBuffer) FillStateUpdate

func (b *SyncedBuffer) FillStateUpdate(update *pb.BufferStateUpdate)

func (*SyncedBuffer) Free

func (b *SyncedBuffer) Free() int

func (*SyncedBuffer) HasUnsentData

func (b *SyncedBuffer) HasUnsentData() bool

func (*SyncedBuffer) Len

func (b *SyncedBuffer) Len() int

func (*SyncedBuffer) NextSendSeq

func (b *SyncedBuffer) NextSendSeq() uint64

func (*SyncedBuffer) ResetNextSeq

func (b *SyncedBuffer) ResetNextSeq()

func (*SyncedBuffer) SendData

func (b *SyncedBuffer) SendData(numBytes int)

func (*SyncedBuffer) StartSeq

func (b *SyncedBuffer) StartSeq() uint64

func (*SyncedBuffer) Sync

func (b *SyncedBuffer) Sync(req *pb.BufferStateUpdate, reply *pb.BufferStateUpdate)

func (*SyncedBuffer) UpdateState

func (b *SyncedBuffer) UpdateState(req *pb.BufferStateUpdate)

type TCPConnMap

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

func MakeTCPConnMap

func MakeTCPConnMap() *TCPConnMap

func (*TCPConnMap) GetState

func (m *TCPConnMap) GetState(ip net.IP, port uint16) *TCPConnState

func (*TCPConnMap) PutState

func (m *TCPConnMap) PutState(ip net.IP, port uint16, state *TCPConnState)

type TCPConnState

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

func NewTCPConnState

func NewTCPConnState(localPort, remotePort uint16, sender TCPSender, remoteAddr *net.IPAddr) *TCPConnState

func (*TCPConnState) AppendAt

func (s *TCPConnState) AppendAt(b []byte, offset uint64) (int, error)

func (*TCPConnState) Close

func (s *TCPConnState) Close() error

func (*TCPConnState) ConsumePacket

func (s *TCPConnState) ConsumePacket(hdr *TCPHeader, data []byte) error

func (*TCPConnState) ConsumeThenRead

func (s *TCPConnState) ConsumeThenRead(b []byte, offset uint64) (int, error)

func (*TCPConnState) Read

func (s *TCPConnState) Read(b []byte) (int, error)

func (*TCPConnState) RemoteIPAddr

func (s *TCPConnState) RemoteIPAddr() *net.IPAddr

func (*TCPConnState) RemotePort

func (s *TCPConnState) RemotePort() uint16

func (*TCPConnState) Sync

func (s *TCPConnState) Sync(req *pb.SyncRequest, reply *pb.SyncReply) error

func (*TCPConnState) Write

func (s *TCPConnState) Write(b []byte) (int, error)

type TCPHeader

type TCPHeader struct {
	SrcPort       uint16
	DstPort       uint16
	SeqNum        uint32
	AckNum        uint32
	DataOff       int
	Fin           bool
	Syn           bool
	Rst           bool
	Psh           bool
	Ack           bool
	Urg           bool
	WindowSize    int
	Checksum      uint16
	UrgentPointer int
	Options       []byte
	Timestamp     *TCPTimestamp
}

func ParseTCPHeader

func ParseTCPHeader(buf []byte) (*TCPHeader, error)

func (*TCPHeader) MarshalInto

func (h *TCPHeader) MarshalInto(buf []byte) (int, error)

func (*TCPHeader) MarshalSize

func (h *TCPHeader) MarshalSize() int

func (*TCPHeader) String

func (h *TCPHeader) String() string

type TCPSender

type TCPSender interface {
	SendTCPPacket(b []byte, addr *net.IPAddr, port uint16) error
	SendSyncRequest(req *pb.SyncRequest, reply *pb.SyncReply) error
}

type TCPStack

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

func NewTCPStack

func NewTCPStack(ipSender IPSender, localAddr *net.IPAddr) *TCPStack

func (*TCPStack) GetConn

func (s *TCPStack) GetConn(remoteAddr *net.IPAddr, remotePort uint16) *TCPConnState

func (*TCPStack) HandleIPPacket

func (s *TCPStack) HandleIPPacket(packet *IPPacket) error

func (*TCPStack) Listen

func (s *TCPStack) Listen() (*TCPConnState, error)

func (*TCPStack) SendSyncRequest

func (s *TCPStack) SendSyncRequest(req *pb.SyncRequest, reply *pb.SyncReply) error

func (*TCPStack) SendTCPPacket

func (s *TCPStack) SendTCPPacket(b []byte, addr *net.IPAddr, port uint16) error

func (*TCPStack) SetSyncClient

func (s *TCPStack) SetSyncClient(c *SyncClient)

func (*TCPStack) Sync

func (s *TCPStack) Sync(req *pb.SyncRequest, reply *pb.SyncReply) error

type TCPTimestamp

type TCPTimestamp struct {
	Sender    uint32
	EchoReply uint32
}

func (*TCPTimestamp) String

func (t *TCPTimestamp) String() string

Directories

Path Synopsis
cmd
test command

Jump to

Keyboard shortcuts

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