jrouter/peer.go

370 lines
9.7 KiB
Go
Raw Normal View History

2024-03-31 09:31:50 +11:00
/*
Copyright 2024 Josh Deprez
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
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
2024-04-01 14:44:51 +11:00
// Send an RI-Req
if _, err := p.send(p.tr.NewRIReqPacket()); err != nil {
log.Printf("Couldn't send RI-Req packet: %v", err)
return err
}
2024-04-01 14:56:49 +11:00
rstate = rsWaitForRIRsp
2024-03-30 14:13:34 +11:00
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,
2024-03-30 20:53:58 +11:00
Distance: 0,
2024-03-30 20:27:24 +11:00
},
}
2024-03-30 20:35:19 +11:00
p.tr.LocalSeq = 1
if _, err := p.send(p.tr.NewRIRspPacket(aurp.RoutingFlagLast, nets)); err != nil {
2024-03-30 20:27:24 +11:00
log.Printf("Couldn't send RI-Rsp packet: %v", err)
2024-04-01 14:56:49 +11:00
return err
2024-03-30 20:27:24 +11:00
}
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-04-01 14:56:49 +11:00
log.Printf("Learned about these networks: %v", pkt.Networks)
2024-03-30 14:13:34 +11:00
// TODO: Integrate info into route table
2024-04-01 14:56:49 +11:00
// TODO: track which networks we don't have zone info for, and
// only set SZI for those ?
if _, err := p.send(p.tr.NewRIAckPacket(pkt.ConnectionID, pkt.Sequence, aurp.RoutingFlagSendZoneInfo)); err != nil {
log.Printf("Couldn't send RI-Ack packet: %v", err)
return err
}
2024-04-01 15:09:26 +11:00
if pkt.Flags&aurp.RoutingFlagLast != 0 {
// No longer waiting for an RI-Rsp
rstate = rsConnected
}
2024-04-01 14:56:49 +11:00
2024-03-30 14:13:34 +11:00
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)
2024-04-01 14:56:49 +11:00
// TODO: only respond with zones for networks that were in the
// RI-Rsp that corresponded to this RI-Ack
2024-03-30 20:27:24 +11:00
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:
2024-04-01 14:56:49 +11:00
// TODO: only respond with zones for networks specified by the
// ZI-Req
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)
}
2024-03-30 14:13:34 +11:00
case *aurp.ZIRspPacket:
// TODO: Integrate info into zone table
2024-04-01 14:56:49 +11:00
log.Printf("Learned about these zones: %v", pkt.Zones)
2024-03-30 14:13:34 +11:00
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
}
}
}
}