Tickle handling
This commit is contained in:
parent
b9ae3cc426
commit
520046d86a
4 changed files with 146 additions and 86 deletions
|
@ -1,14 +1,26 @@
|
||||||
package aurp
|
package aurp
|
||||||
|
|
||||||
type ErrorCode = int16
|
type ErrorCode int16
|
||||||
|
|
||||||
// Various error codes.
|
// Various error codes.
|
||||||
const (
|
const (
|
||||||
ErrCodeNormalClose = -1
|
ErrCodeNormalClose ErrorCode = -1
|
||||||
ErrCodeRoutingLoop = -2
|
ErrCodeRoutingLoop ErrorCode = -2
|
||||||
ErrCodeOutOfSync = -3
|
ErrCodeOutOfSync ErrorCode = -3
|
||||||
ErrCodeOptionNegotiation = -4
|
ErrCodeOptionNegotiation ErrorCode = -4
|
||||||
ErrCodeInvalidVersion = -5
|
ErrCodeInvalidVersion ErrorCode = -5
|
||||||
ErrCodeInsufficientResources = -6
|
ErrCodeInsufficientResources ErrorCode = -6
|
||||||
ErrCodeAuthentication = -7
|
ErrCodeAuthentication ErrorCode = -7
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (e ErrorCode) String() string {
|
||||||
|
return map[ErrorCode]string{
|
||||||
|
ErrCodeNormalClose: "normal connection close",
|
||||||
|
ErrCodeRoutingLoop: "routing loop detected",
|
||||||
|
ErrCodeOutOfSync: "connection out of sync",
|
||||||
|
ErrCodeOptionNegotiation: "option-negotiation error",
|
||||||
|
ErrCodeInvalidVersion: "invalid version number",
|
||||||
|
ErrCodeInsufficientResources: "insufficient resources for connection",
|
||||||
|
ErrCodeAuthentication: "authentication error",
|
||||||
|
}[e]
|
||||||
|
}
|
||||||
|
|
|
@ -110,3 +110,23 @@ func (tr *Transport) NewOpenRspPacket(envFlags RoutingFlag, rateOrErr int16, opt
|
||||||
Options: opts,
|
Options: opts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tr *Transport) NewTicklePacket() *TicklePacket {
|
||||||
|
return &TicklePacket{
|
||||||
|
Header: Header{
|
||||||
|
TrHeader: tr.transaction(tr.LocalConnID),
|
||||||
|
CommandCode: CmdCodeTickle,
|
||||||
|
Flags: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *Transport) NewTickleAckPacket() *TickleAckPacket {
|
||||||
|
return &TickleAckPacket{
|
||||||
|
Header: Header{
|
||||||
|
TrHeader: tr.transaction(tr.RemoteConnID),
|
||||||
|
CommandCode: CmdCodeTickleAck,
|
||||||
|
Flags: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
7
main.go
7
main.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
@ -17,6 +18,8 @@ func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
log.Println("jrouter")
|
log.Println("jrouter")
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
cfg, err := loadConfig(*configFilePath)
|
cfg, err := loadConfig(*configFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Couldn't load configuration file: %v", err)
|
log.Fatalf("Couldn't load configuration file: %v", err)
|
||||||
|
@ -83,7 +86,7 @@ func main() {
|
||||||
raddr: raddr,
|
raddr: raddr,
|
||||||
recv: make(chan aurp.Packet, 1024),
|
recv: make(chan aurp.Packet, 1024),
|
||||||
}
|
}
|
||||||
go peer.handle()
|
go peer.handle(ctx)
|
||||||
|
|
||||||
peers[udpAddrFromNet(raddr)] = peer
|
peers[udpAddrFromNet(raddr)] = peer
|
||||||
}
|
}
|
||||||
|
@ -127,7 +130,7 @@ func main() {
|
||||||
recv: make(chan aurp.Packet, 1024),
|
recv: make(chan aurp.Packet, 1024),
|
||||||
}
|
}
|
||||||
peers[ra] = pr
|
peers[ra] = pr
|
||||||
go pr.handle()
|
go pr.handle(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass the packet to the goroutine in charge of this peer.
|
// Pass the packet to the goroutine in charge of this peer.
|
||||||
|
|
177
peer.go
177
peer.go
|
@ -2,8 +2,10 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitea.drjosh.dev/josh/jrouter/aurp"
|
"gitea.drjosh.dev/josh/jrouter/aurp"
|
||||||
)
|
)
|
||||||
|
@ -13,6 +15,8 @@ type peer struct {
|
||||||
conn *net.UDPConn
|
conn *net.UDPConn
|
||||||
raddr *net.UDPAddr
|
raddr *net.UDPAddr
|
||||||
recv chan aurp.Packet
|
recv chan aurp.Packet
|
||||||
|
|
||||||
|
lastHeardFrom time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// send encodes and sends pkt to the remote host.
|
// send encodes and sends pkt to the remote host.
|
||||||
|
@ -24,94 +28,115 @@ func (p *peer) send(pkt aurp.Packet) (int, error) {
|
||||||
return p.conn.WriteToUDP(b.Bytes(), p.raddr)
|
return p.conn.WriteToUDP(b.Bytes(), p.raddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *peer) handle() {
|
func (p *peer) handle(ctx context.Context) error {
|
||||||
|
ticker := time.NewTicker(1 * time.Second)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
// Write an Open-Req packet
|
// Write an Open-Req packet
|
||||||
n, err := p.send(p.tr.NewOpenReqPacket(nil))
|
n, err := p.send(p.tr.NewOpenReqPacket(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Couldn't send Open-Req packet: %v", err)
|
log.Printf("Couldn't send Open-Req packet: %v", err)
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
log.Printf("Sent Open-Req (len %d) to peer %v", n, p.raddr)
|
log.Printf("Sent Open-Req (len %d) to peer %v", n, p.raddr)
|
||||||
|
|
||||||
for pkt := range p.recv {
|
for {
|
||||||
switch pkt := pkt.(type) {
|
select {
|
||||||
case *aurp.AppleTalkPacket:
|
case <-ctx.Done():
|
||||||
// Probably something like:
|
return ctx.Err()
|
||||||
//
|
|
||||||
// * parse the DDP header
|
|
||||||
// * check that this is headed for our local network
|
|
||||||
// * write the packet out in an EtherTalk frame
|
|
||||||
//
|
|
||||||
// or maybe if we were implementing a "central hub"
|
|
||||||
//
|
|
||||||
// * parse the DDP header
|
|
||||||
// * see if we know the network
|
|
||||||
// * forward to the peer with that network and lowest metric
|
|
||||||
|
|
||||||
case *aurp.OpenReqPacket:
|
case <-ticker.C:
|
||||||
// The peer tells us their connection ID in Open-Req.
|
// TODO: time-based state changes
|
||||||
p.tr.RemoteConnID = pkt.ConnectionID
|
// Check LHFT, send tickle?
|
||||||
|
if time.Since(p.lastHeardFrom) > 10*time.Second {
|
||||||
// Formulate a response.
|
if _, err := p.send(p.tr.NewTicklePacket()); err != nil {
|
||||||
var orsp *aurp.OpenRspPacket
|
log.Printf("Couldn't send Tickle: %v", err)
|
||||||
switch {
|
}
|
||||||
case pkt.Version != 1:
|
|
||||||
// Respond with Open-Rsp with unknown version error.
|
|
||||||
orsp = p.tr.NewOpenRspPacket(0, aurp.ErrCodeInvalidVersion, nil)
|
|
||||||
|
|
||||||
case len(pkt.Options) > 0:
|
|
||||||
// Options? OPTIONS? We don't accept no stinkin' _options_
|
|
||||||
orsp = p.tr.NewOpenRspPacket(0, aurp.ErrCodeOptionNegotiation, nil)
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Accept it I guess.
|
|
||||||
orsp = p.tr.NewOpenRspPacket(0, 1, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Responding with %T", orsp)
|
case pkt := <-p.recv:
|
||||||
|
switch pkt := pkt.(type) {
|
||||||
|
case *aurp.AppleTalkPacket:
|
||||||
|
// Probably something like:
|
||||||
|
//
|
||||||
|
// * parse the DDP header
|
||||||
|
// * check that this is headed for our local network
|
||||||
|
// * write the packet out in an EtherTalk frame
|
||||||
|
//
|
||||||
|
// or maybe if we were implementing a "central hub"
|
||||||
|
//
|
||||||
|
// * parse the DDP header
|
||||||
|
// * see if we know the network
|
||||||
|
// * forward to the peer with that network and lowest metric
|
||||||
|
|
||||||
if _, err := p.send(orsp); err != nil {
|
case *aurp.OpenReqPacket:
|
||||||
log.Printf("Couldn't send Open-Rsp: %v", err)
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Responding with %T", orsp)
|
||||||
|
|
||||||
|
if _, err := p.send(orsp); err != nil {
|
||||||
|
log.Printf("Couldn't send Open-Rsp: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *aurp.OpenRspPacket:
|
||||||
|
if pkt.RateOrErrCode < 0 {
|
||||||
|
// It's an error code.
|
||||||
|
log.Printf("Open-Rsp error code from peer %v: %d", p.raddr.IP, pkt.RateOrErrCode)
|
||||||
|
// Close the connection
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make other requests
|
||||||
|
|
||||||
|
case *aurp.RIReqPacket:
|
||||||
|
// TODO: Respond with RI-Rsp
|
||||||
|
|
||||||
|
case *aurp.RIRspPacket:
|
||||||
|
// TODO: Repsond with RI-Ack
|
||||||
|
// TODO: Integrate info into route table
|
||||||
|
|
||||||
|
case *aurp.RIAckPacket:
|
||||||
|
// TODO: Continue sending next RI-Rsp (streamed)
|
||||||
|
// TODO: If SZI flag is set, send ZI-Rsp (transaction)
|
||||||
|
|
||||||
|
case *aurp.RIUpdPacket:
|
||||||
|
// TODO: Integrate info into route table
|
||||||
|
|
||||||
|
case *aurp.RDPacket:
|
||||||
|
// TODO: Remove router from tables
|
||||||
|
// TODO: Close connection
|
||||||
|
log.Printf("Router Down: error code %d %s", pkt.ErrorCode, pkt.ErrorCode)
|
||||||
|
|
||||||
|
case *aurp.ZIReqPacket:
|
||||||
|
// TODO: Respond with ZI-Rsp
|
||||||
|
|
||||||
|
case *aurp.ZIRspPacket:
|
||||||
|
// TODO: Integrate info into zone table
|
||||||
|
|
||||||
|
case *aurp.TicklePacket:
|
||||||
|
if _, err := p.send(p.tr.NewTickleAckPacket()); err != nil {
|
||||||
|
log.Printf("Couldn't send Tickle-Ack: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *aurp.TickleAckPacket:
|
||||||
|
p.lastHeardFrom = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
case *aurp.OpenRspPacket:
|
|
||||||
if pkt.RateOrErrCode < 0 {
|
|
||||||
// It's an error code.
|
|
||||||
log.Printf("Open-Rsp error code from peer %v: %d", p.raddr.IP, pkt.RateOrErrCode)
|
|
||||||
// Close the connection
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make other requests
|
|
||||||
|
|
||||||
case *aurp.RIReqPacket:
|
|
||||||
// TODO: Respond with RI-Rsp
|
|
||||||
|
|
||||||
case *aurp.RIRspPacket:
|
|
||||||
// TODO: Repsond with RI-Ack
|
|
||||||
// TODO: Integrate info into route table
|
|
||||||
|
|
||||||
case *aurp.RIAckPacket:
|
|
||||||
// TODO: Continue sending next RI-Rsp (streamed)
|
|
||||||
// TODO: If SZI flag is set, send ZI-Rsp (transaction)
|
|
||||||
|
|
||||||
case *aurp.RIUpdPacket:
|
|
||||||
// TODO: Integrate info into route table
|
|
||||||
|
|
||||||
case *aurp.RDPacket:
|
|
||||||
// TODO: Remove router from tables
|
|
||||||
// TODO: Close connection
|
|
||||||
|
|
||||||
case *aurp.ZIReqPacket:
|
|
||||||
// TODO: Respond with ZI-Rsp
|
|
||||||
|
|
||||||
case *aurp.ZIRspPacket:
|
|
||||||
// TODO: Integrate info into zone table
|
|
||||||
|
|
||||||
case *aurp.TicklePacket:
|
|
||||||
// TODO: Respond with TickleAck
|
|
||||||
|
|
||||||
case *aurp.TickleAckPacket:
|
|
||||||
// TODO: Reset LHFT
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue