jrouter/peer.go

321 lines
8.1 KiB
Go
Raw Normal View History

2024-03-25 19:30:06 +11:00
package main
import (
"bytes"
2024-03-30 14:13:34 +11:00
"context"
2024-03-25 19:30:06 +11:00
"log"
"net"
2024-03-30 14:13:34 +11:00
"time"
2024-03-25 19:30:06 +11:00
"gitea.drjosh.dev/josh/jrouter/aurp"
)
2024-03-30 16:43:14 +11:00
const (
// TODO: check these parameters
2024-03-30 16:48:47 +11:00
lastHeardFromTimer = 10 * time.Second
tickleRetryLimit = 10
sendRetryTimer = 10 * time.Second
sendRetryLimit = 5
2024-03-30 16:43:14 +11:00
)
2024-03-30 16:14:18 +11:00
type receiverState int
const (
2024-03-30 16:48:47 +11:00
rsUnconnected receiverState = iota
rsConnected
rsWaitForOpenRsp
rsWaitForRIRsp
rsWaitForTickleAck
2024-03-30 16:14:18 +11:00
)
2024-03-30 18:01:28 +11:00
func (rs receiverState) String() string {
return map[receiverState]string{
rsUnconnected: "unconnected",
rsConnected: "connected",
rsWaitForOpenRsp: "waiting for Open-Rsp",
rsWaitForRIRsp: "waiting for RI-Rsp",
rsWaitForTickleAck: "waiting for Tickle-Ack",
}[rs]
}
2024-03-30 16:14:18 +11:00
type senderState int
const (
2024-03-30 16:48:47 +11:00
ssUnconnected senderState = iota
ssConnected
ssWaitForRIAck1
ssWaitForRIAck2
ssWaitForRIAck3
2024-03-30 16:14:18 +11:00
)
2024-03-30 18:01:28 +11:00
func (ss senderState) String() string {
return map[senderState]string{
ssUnconnected: "unconnected",
ssConnected: "connected",
ssWaitForRIAck1: "waiting for RI-Ack (1)",
ssWaitForRIAck2: "waiting for RI-Ack (2)",
ssWaitForRIAck3: "waiting for RI-Ack (3)",
}[ss]
}
2024-03-25 19:30:06 +11:00
type peer struct {
2024-03-30 20:27:24 +11:00
cfg *config
2024-03-25 19:30:06 +11:00
tr *aurp.Transport
conn *net.UDPConn
raddr *net.UDPAddr
recv chan aurp.Packet
}
// send encodes and sends pkt to the remote host.
func (p *peer) send(pkt aurp.Packet) (int, error) {
var b bytes.Buffer
if _, err := pkt.WriteTo(&b); err != nil {
return 0, err
}
2024-03-30 16:43:14 +11:00
log.Printf("Sending %T (len %d) to %v", pkt, b.Len(), p.raddr)
2024-03-25 19:30:06 +11:00
return p.conn.WriteToUDP(b.Bytes(), p.raddr)
}
2024-03-30 14:13:34 +11:00
func (p *peer) handle(ctx context.Context) error {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
2024-03-30 15:26:23 +11:00
lastHeardFrom := time.Now()
2024-03-30 16:43:14 +11:00
lastSend := time.Now()
sendRetries := 0
2024-03-30 14:24:16 +11:00
2024-03-30 16:48:47 +11:00
rstate := rsUnconnected
sstate := ssUnconnected
2024-03-30 16:14:18 +11:00
2024-03-25 19:30:06 +11:00
// Write an Open-Req packet
2024-03-30 16:43:14 +11:00
if _, err := p.send(p.tr.NewOpenReqPacket(nil)); err != nil {
2024-03-25 19:30:06 +11:00
log.Printf("Couldn't send Open-Req packet: %v", err)
2024-03-30 14:13:34 +11:00
return err
2024-03-25 19:30:06 +11:00
}
2024-03-30 16:48:47 +11:00
rstate = rsWaitForOpenRsp
2024-03-30 16:14:18 +11:00
2024-03-30 14:13:34 +11:00
for {
select {
case <-ctx.Done():
2024-03-30 16:48:47 +11:00
if sstate == ssUnconnected {
// Return immediately
2024-03-30 16:14:18 +11:00
return ctx.Err()
}
2024-03-30 14:24:16 +11:00
// Send a best-effort Router Down before returning
if _, err := p.send(p.tr.NewRDPacket(aurp.ErrCodeNormalClose)); err != nil {
log.Printf("Couldn't send RD packet: %v", err)
}
2024-03-30 14:13:34 +11:00
return ctx.Err()
case <-ticker.C:
2024-03-30 16:43:14 +11:00
switch rstate {
2024-03-30 16:48:47 +11:00
case rsWaitForOpenRsp:
2024-03-30 16:43:14 +11:00
if time.Since(lastSend) <= sendRetryTimer {
break
}
if sendRetries >= sendRetryLimit {
log.Printf("Send retry limit reached while waiting for Open-Rsp, closing connection")
2024-03-30 16:48:47 +11:00
rstate = rsUnconnected
2024-03-30 16:43:14 +11:00
break
}
// Send another Open-Req
sendRetries++
2024-03-30 16:48:47 +11:00
lastSend = time.Now()
2024-03-30 16:43:14 +11:00
if _, err := p.send(p.tr.NewOpenReqPacket(nil)); err != nil {
log.Printf("Couldn't send Open-Req packet: %v", err)
return err
}
2024-03-30 16:48:47 +11:00
case rsConnected:
2024-03-30 16:14:18 +11:00
// Check LHFT, send tickle?
2024-03-30 16:48:47 +11:00
if time.Since(lastHeardFrom) <= lastHeardFromTimer {
break
}
if _, err := p.send(p.tr.NewTicklePacket()); err != nil {
log.Printf("Couldn't send Tickle: %v", err)
return err
}
rstate = rsWaitForTickleAck
sendRetries = 0
lastSend = time.Now()
case rsWaitForTickleAck:
if time.Since(lastSend) <= sendRetryTimer {
break
}
if sendRetries >= tickleRetryLimit {
log.Printf("Send retry limit reached while waiting for Tickle-Ack, closing connection")
rstate = rsUnconnected
break
}
sendRetries++
lastSend = time.Now()
if _, err := p.send(p.tr.NewTicklePacket()); err != nil {
log.Printf("Couldn't send Tickle: %v", err)
return err
2024-03-30 14:13:34 +11:00
}
2024-03-25 19:30:06 +11:00
}
2024-03-30 14:13:34 +11:00
case pkt := <-p.recv:
2024-03-30 15:26:23 +11:00
lastHeardFrom = time.Now()
2024-03-30 14:13:34 +11:00
switch pkt := pkt.(type) {
case *aurp.OpenReqPacket:
2024-03-30 16:48:47 +11:00
if sstate != ssUnconnected {
2024-03-30 18:01:28 +11:00
log.Printf("Open-Req received but sender state is not unconnected (was %v)", sstate)
2024-03-30 16:14:18 +11:00
}
2024-03-30 14:13:34 +11:00
// The peer tells us their connection ID in Open-Req.
p.tr.RemoteConnID = pkt.ConnectionID
// Formulate a response.
var orsp *aurp.OpenRspPacket
switch {
case pkt.Version != 1:
// Respond with Open-Rsp with unknown version error.
orsp = p.tr.NewOpenRspPacket(0, int16(aurp.ErrCodeInvalidVersion), nil)
case len(pkt.Options) > 0:
// Options? OPTIONS? We don't accept no stinkin' _options_
orsp = p.tr.NewOpenRspPacket(0, int16(aurp.ErrCodeOptionNegotiation), nil)
default:
// Accept it I guess.
orsp = p.tr.NewOpenRspPacket(0, 1, nil)
}
if _, err := p.send(orsp); err != nil {
log.Printf("Couldn't send Open-Rsp: %v", err)
2024-03-30 16:48:47 +11:00
return err
2024-03-30 14:13:34 +11:00
}
2024-03-30 16:14:18 +11:00
if orsp.RateOrErrCode >= 0 {
2024-03-30 16:48:47 +11:00
sstate = ssConnected
2024-03-30 16:14:18 +11:00
}
2024-03-30 14:13:34 +11:00
2024-03-30 18:53:31 +11:00
// If receiver is unconnected, commence connecting
if rstate == rsUnconnected {
lastSend = time.Now()
sendRetries = 0
if _, err := p.send(p.tr.NewOpenReqPacket(nil)); err != nil {
log.Printf("Couldn't send Open-Req packet: %v", err)
return err
}
rstate = rsWaitForOpenRsp
}
2024-03-30 14:13:34 +11:00
case *aurp.OpenRspPacket:
2024-03-30 16:48:47 +11:00
if rstate != rsWaitForOpenRsp {
2024-03-30 18:01:28 +11:00
log.Printf("Received Open-Rsp but was not waiting for one (receiver state was %v)", rstate)
2024-03-30 16:14:18 +11:00
}
2024-03-30 14:13:34 +11:00
if pkt.RateOrErrCode < 0 {
// It's an error code.
log.Printf("Open-Rsp error code from peer %v: %d", p.raddr.IP, pkt.RateOrErrCode)
2024-03-30 16:48:47 +11:00
rstate = rsUnconnected
2024-03-30 18:01:28 +11:00
break
2024-03-30 14:13:34 +11:00
}
2024-03-30 18:01:28 +11:00
log.Printf("Data receiver is connected!")
rstate = rsConnected
2024-03-30 14:13:34 +11:00
// TODO: Make other requests
case *aurp.RIReqPacket:
2024-03-30 18:01:28 +11:00
if sstate != ssConnected {
log.Printf("Received RI-Req but was not expecting one (sender state was %v)", sstate)
}
2024-03-30 18:53:31 +11:00
2024-03-30 20:27:24 +11:00
nets := aurp.NetworkTuples{
{
RangeStart: p.cfg.EtherTalk.NetStart,
RangeEnd: p.cfg.EtherTalk.NetEnd,
Distance: 1,
},
}
if _, err := p.send(p.tr.NewRIRspPacket(pkt.ConnectionID, p.tr.LocalSeq, aurp.RoutingFlagLast, nets)); err != nil {
log.Printf("Couldn't send RI-Rsp packet: %v", err)
}
sstate = ssWaitForRIAck1
2024-03-30 14:13:34 +11:00
case *aurp.RIRspPacket:
2024-03-30 18:01:28 +11:00
if rstate != rsWaitForRIRsp {
log.Printf("Received RI-Rsp but was not waiting for one (receiver state was %v)", rstate)
}
2024-03-30 14:13:34 +11:00
// TODO: Repsond with RI-Ack
// TODO: Integrate info into route table
case *aurp.RIAckPacket:
2024-03-30 20:27:24 +11:00
switch sstate {
case ssWaitForRIAck1:
// We sent an RI-Rsp, this is the RI-Ack we expected.
case ssWaitForRIAck2:
// We sent an RI-Upd, this is the RI-Ack we expected.
case ssWaitForRIAck3:
// We sent an RD... Why are we here?
continue
default:
log.Printf("Received RI-Ack but was not waiting for one (sender state was %v)", sstate)
}
sstate = ssConnected
// If SZI flag is set, send ZI-Rsp (transaction)
if pkt.Flags&aurp.RoutingFlagSendZoneInfo != 0 {
zones := aurp.ZoneTuples{
{
Network: p.cfg.EtherTalk.NetStart,
Name: p.cfg.EtherTalk.ZoneName,
},
}
if _, err := p.send(p.tr.NewZIRspPacket(zones)); err != nil {
log.Printf("Couldn't send ZI-Rsp packet: %v", err)
}
}
// TODO: Continue sending next RI-Rsp (streamed)?
2024-03-30 14:13:34 +11:00
case *aurp.RIUpdPacket:
// TODO: Integrate info into route table
case *aurp.RDPacket:
2024-03-30 18:01:28 +11:00
if rstate == rsUnconnected || rstate == rsWaitForOpenRsp {
log.Printf("Received RD but was not expecting one (receiver state was %v)", rstate)
}
2024-03-30 16:16:46 +11:00
// TODO: Remove router from route tables
2024-03-30 14:13:34 +11:00
log.Printf("Router Down: error code %d %s", pkt.ErrorCode, pkt.ErrorCode)
2024-03-30 16:14:18 +11:00
// Respond with RI-Ack
if _, err := p.send(p.tr.NewRIAckPacket(pkt.ConnectionID, pkt.Sequence, 0)); err != nil {
log.Printf("Couldn't send RI-Ack: %v", err)
2024-03-30 16:48:47 +11:00
return err
2024-03-30 16:14:18 +11:00
}
2024-03-30 16:16:46 +11:00
// Connection closed
2024-03-30 16:48:47 +11:00
rstate = rsUnconnected
2024-03-30 14:13:34 +11:00
case *aurp.ZIReqPacket:
// TODO: Respond with ZI-Rsp
case *aurp.ZIRspPacket:
// TODO: Integrate info into zone table
case *aurp.TicklePacket:
2024-03-30 15:26:23 +11:00
// Immediately respond with Tickle-Ack
2024-03-30 14:13:34 +11:00
if _, err := p.send(p.tr.NewTickleAckPacket()); err != nil {
log.Printf("Couldn't send Tickle-Ack: %v", err)
2024-03-30 16:48:47 +11:00
return err
2024-03-30 14:13:34 +11:00
}
case *aurp.TickleAckPacket:
2024-03-30 16:48:47 +11:00
if rstate != rsWaitForTickleAck {
2024-03-30 18:01:28 +11:00
log.Printf("Received Tickle-Ack but was not waiting for one (receiver state was %v)", rstate)
2024-03-30 16:14:18 +11:00
}
2024-03-30 16:48:47 +11:00
rstate = rsConnected
2024-03-25 19:30:06 +11:00
}
}
}
}