WIP
This commit is contained in:
parent
b43fabc5b6
commit
19f9d1c891
5 changed files with 168 additions and 55 deletions
|
@ -91,7 +91,7 @@ type Packet interface {
|
||||||
//
|
//
|
||||||
// (This function contains the big switch statement.)
|
// (This function contains the big switch statement.)
|
||||||
func ParsePacket(p []byte) (Packet, error) {
|
func ParsePacket(p []byte) (Packet, error) {
|
||||||
dh, p, err := parseDomainHeader(p)
|
dh, p, err := ParseDomainHeader(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,9 @@ func (dh *DomainHeader) WriteTo(w io.Writer) (int64, error) {
|
||||||
return a.ret()
|
return a.ret()
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseDomainHeader parses a domain header, returning the DH and the remainder
|
// ParseDomainHeader parses a domain header, returning the DH and the remainder
|
||||||
// of the input slice. It does not validate the version or packet type fields.
|
// of the input slice. It does not validate the version or packet type fields.
|
||||||
func parseDomainHeader(b []byte) (DomainHeader, []byte, error) {
|
func ParseDomainHeader(b []byte) (DomainHeader, []byte, error) {
|
||||||
ddi, b, err := parseDomainIdentifier(b)
|
ddi, b, err := parseDomainIdentifier(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DomainHeader{}, b, err
|
return DomainHeader{}, b, err
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package aurp
|
package aurp
|
||||||
|
|
||||||
type ErrorCode int16
|
type ErrorCode = int16
|
||||||
|
|
||||||
// Various error codes.
|
// Various error codes.
|
||||||
const (
|
const (
|
||||||
ErrCodeNormalClose ErrorCode = -1
|
ErrCodeNormalClose = -1
|
||||||
ErrCodeRoutingLoop ErrorCode = -2
|
ErrCodeRoutingLoop = -2
|
||||||
ErrCodeOutOfSync ErrorCode = -3
|
ErrCodeOutOfSync = -3
|
||||||
ErrCodeOptionNegotiation ErrorCode = -4
|
ErrCodeOptionNegotiation = -4
|
||||||
ErrCodeInvalidVersion ErrorCode = -5
|
ErrCodeInvalidVersion = -5
|
||||||
ErrCodeInsufficientResources ErrorCode = -6
|
ErrCodeInsufficientResources = -6
|
||||||
ErrCodeAuthentication ErrorCode = -7
|
ErrCodeAuthentication = -7
|
||||||
)
|
)
|
||||||
|
|
|
@ -34,62 +34,62 @@ func parseTrHeader(p []byte) (TrHeader, []byte, error) {
|
||||||
}, p[4:], nil
|
}, p[4:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// incSequence increments the sequence number.
|
type Transport struct {
|
||||||
// Note that 0 is special and 65535+1 = 1, according to AURP.
|
// LocalDI and RemoteDI are used for producing packets.
|
||||||
func (tr *TrHeader) incSequence() {
|
// When sending a packet, we use LocalDI as SourceDI and RemoteDI as
|
||||||
tr.Sequence++
|
// DestinationDI.
|
||||||
if tr.Sequence == 0 {
|
// (When receiving a packet, we expect to see LocalDI as DestinationDI
|
||||||
tr.Sequence = 1
|
// - but it might not be - and we expect to see RemoteDI as SourceDI.)
|
||||||
|
LocalDI, RemoteDI DomainIdentifier
|
||||||
|
|
||||||
|
// LocalConnID is used for packets sent in the role of data receiver.
|
||||||
|
// RemoteConnID is used for packets sent in the role of data sender.
|
||||||
|
LocalConnID, RemoteConnID uint16
|
||||||
|
|
||||||
|
// LocalSeq is used for packets sent (as data sender)
|
||||||
|
// RemoteSeq is used to check packets received (remote is the data sender).
|
||||||
|
LocalSeq, RemoteSeq uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// domainHeader returns a new domain header suitable for sending a packet.
|
||||||
|
func (tr *Transport) domainHeader(pt PacketType) DomainHeader {
|
||||||
|
return DomainHeader{
|
||||||
|
DestinationDI: tr.RemoteDI,
|
||||||
|
SourceDI: tr.LocalDI,
|
||||||
|
Version: 1,
|
||||||
|
Reserved: 0,
|
||||||
|
PacketType: pt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// transaction returns a new TrHeader based on this one, usable for transaction
|
// transaction returns a new TrHeader, usable for transaction requests or
|
||||||
// requests or responses.
|
// responses. Both data senders and data receivers can send transactions.
|
||||||
func (tr *TrHeader) transaction() TrHeader {
|
// It should be given one of tr.LocalConnID (as data receiver) or
|
||||||
|
// tr.RemoteConnID (as data sender).
|
||||||
|
func (tr *Transport) transaction(connID uint16) TrHeader {
|
||||||
return TrHeader{
|
return TrHeader{
|
||||||
DomainHeader: DomainHeader{
|
DomainHeader: tr.domainHeader(PacketTypeRouting),
|
||||||
DestinationDI: tr.DestinationDI,
|
ConnectionID: connID,
|
||||||
SourceDI: tr.SourceDI,
|
|
||||||
Version: 1,
|
|
||||||
Reserved: 0,
|
|
||||||
PacketType: PacketTypeRouting,
|
|
||||||
},
|
|
||||||
ConnectionID: tr.ConnectionID,
|
|
||||||
Sequence: 0, // Transaction packets all use sequence number 0.
|
Sequence: 0, // Transaction packets all use sequence number 0.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataSender is used to track sender state in a one-way AURP connection.
|
// sequenced returns a new TrHeader usable for sending a sequenced data packet.
|
||||||
// Note that both data senders and data recievers can send packets.
|
// Only data senders send sequenced data.
|
||||||
type DataSender struct {
|
func (tr *Transport) sequenced(connID, seq uint16) TrHeader {
|
||||||
TrHeader
|
return TrHeader{
|
||||||
|
DomainHeader: tr.domainHeader(PacketTypeRouting),
|
||||||
|
ConnectionID: connID,
|
||||||
|
Sequence: seq,
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOpenRspPacket returns a new Open-Rsp packet structure.
|
|
||||||
func (ds *DataSender) NewOpenRspPacket(envFlags RoutingFlag, rateOrErr int16, opts Options) *OpenRspPacket {
|
|
||||||
return &OpenRspPacket{
|
|
||||||
Header: Header{
|
|
||||||
TrHeader: ds.transaction(),
|
|
||||||
CommandCode: CmdCodeOpenRsp,
|
|
||||||
Flags: envFlags,
|
|
||||||
},
|
|
||||||
RateOrErrCode: rateOrErr,
|
|
||||||
Options: opts,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataReceiver is used to track reciever state in a one-way AURP connection.
|
|
||||||
// Note that both data senders and data recievers can send packets.
|
|
||||||
type DataReceiver struct {
|
|
||||||
TrHeader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOpenReqPacket returns a new Open-Req packet structure. By default it sets
|
// NewOpenReqPacket returns a new Open-Req packet structure. By default it sets
|
||||||
// all SUI flags and uses version 1.
|
// all SUI flags and uses version 1.
|
||||||
func (dr *DataReceiver) NewOpenReqPacket(opts Options) *OpenReqPacket {
|
func (tr *Transport) NewOpenReqPacket(opts Options) *OpenReqPacket {
|
||||||
return &OpenReqPacket{
|
return &OpenReqPacket{
|
||||||
Header: Header{
|
Header: Header{
|
||||||
TrHeader: dr.transaction(),
|
TrHeader: tr.transaction(tr.LocalConnID),
|
||||||
CommandCode: CmdCodeOpenReq,
|
CommandCode: CmdCodeOpenReq,
|
||||||
Flags: RoutingFlagAllSUI,
|
Flags: RoutingFlagAllSUI,
|
||||||
},
|
},
|
||||||
|
@ -97,3 +97,16 @@ func (dr *DataReceiver) NewOpenReqPacket(opts Options) *OpenReqPacket {
|
||||||
Options: opts,
|
Options: opts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewOpenRspPacket returns a new Open-Rsp packet structure.
|
||||||
|
func (tr *Transport) NewOpenRspPacket(envFlags RoutingFlag, rateOrErr int16, opts Options) *OpenRspPacket {
|
||||||
|
return &OpenRspPacket{
|
||||||
|
Header: Header{
|
||||||
|
TrHeader: tr.transaction(tr.RemoteConnID),
|
||||||
|
CommandCode: CmdCodeOpenRsp,
|
||||||
|
Flags: envFlags,
|
||||||
|
},
|
||||||
|
RateOrErrCode: rateOrErr,
|
||||||
|
Options: opts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
106
main.go
106
main.go
|
@ -1,15 +1,50 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"gitea.drjosh.dev/josh/jrouter/aurp"
|
"gitea.drjosh.dev/josh/jrouter/aurp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var localIPAddr = flag.String("local-ip", "", "IPv4 address to use as the Source Domain Identifier")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
log.Println("jrouter")
|
log.Println("jrouter")
|
||||||
|
|
||||||
|
localIP := net.ParseIP(*localIPAddr).To4()
|
||||||
|
if localIP == nil {
|
||||||
|
iaddrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Couldn't read network interface addresses: %v", err)
|
||||||
|
}
|
||||||
|
for _, iaddr := range iaddrs {
|
||||||
|
inet, ok := iaddr.(*net.IPNet)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !inet.IP.IsGlobalUnicast() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
localIP = inet.IP.To4()
|
||||||
|
if localIP != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if localIP == nil {
|
||||||
|
log.Fatalf("No global unicast IPv4 addresses on any network interfaces, and no valid address passed with --local-ip")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Using %v as local domain identifier", localIP)
|
||||||
|
|
||||||
|
peers := make(map[uint32]*aurp.Transport)
|
||||||
|
var nextConnID uint16
|
||||||
|
|
||||||
ln, err := net.ListenUDP("udp4", &net.UDPAddr{Port: 387})
|
ln, err := net.ListenUDP("udp4", &net.UDPAddr{Port: 387})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Couldn't listen on udp4:387: %v", err)
|
log.Fatalf("Couldn't listen on udp4:387: %v", err)
|
||||||
|
@ -18,11 +53,16 @@ func main() {
|
||||||
// Incoming packet loop
|
// Incoming packet loop
|
||||||
pb := make([]byte, 65536)
|
pb := make([]byte, 65536)
|
||||||
for {
|
for {
|
||||||
pktlen, _, readErr := ln.ReadFromUDP(pb)
|
pktlen, raddr, readErr := ln.ReadFromUDP(pb)
|
||||||
// "Callers should always process
|
// net.PacketConn.ReadFrom: "Callers should always process
|
||||||
// the n > 0 bytes returned before considering the error err."
|
// the n > 0 bytes returned before considering the error err."
|
||||||
|
|
||||||
_, parseErr := aurp.ParsePacket(pb[:pktlen])
|
dh, _, err := aurp.ParseDomainHeader(pb[:pktlen])
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to parse domain header: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt, parseErr := aurp.ParsePacket(pb[:pktlen])
|
||||||
if parseErr != nil {
|
if parseErr != nil {
|
||||||
log.Printf("Failed to parse packet: %v", parseErr)
|
log.Printf("Failed to parse packet: %v", parseErr)
|
||||||
}
|
}
|
||||||
|
@ -31,5 +71,65 @@ func main() {
|
||||||
log.Printf("Failed to read packet: %v", readErr)
|
log.Printf("Failed to read packet: %v", readErr)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Existing peer?
|
||||||
|
rip := binary.BigEndian.Uint32(raddr.IP)
|
||||||
|
tr := peers[rip]
|
||||||
|
if tr == nil {
|
||||||
|
// New peer!
|
||||||
|
tr = &aurp.Transport{
|
||||||
|
LocalDI: aurp.IPDomainIdentifier(localIP),
|
||||||
|
RemoteDI: dh.SourceDI,
|
||||||
|
LocalConnID: nextConnID,
|
||||||
|
}
|
||||||
|
nextConnID++
|
||||||
|
peers[rip] = tr
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p := 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
|
||||||
|
|
||||||
|
case *aurp.OpenReqPacket:
|
||||||
|
// The peer tells us their connection ID in Open-Req.
|
||||||
|
tr.RemoteConnID = p.ConnectionID
|
||||||
|
|
||||||
|
// Formulate a response.
|
||||||
|
var rp *aurp.OpenRspPacket
|
||||||
|
if p.Version != 1 {
|
||||||
|
// Respond with Open-Rsp with unknown version error.
|
||||||
|
rp = tr.NewOpenRspPacket(0, aurp.ErrCodeInvalidVersion, nil)
|
||||||
|
} else {
|
||||||
|
// Accept the connection, I guess?
|
||||||
|
rp = tr.NewOpenRspPacket(0, 1, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an Open-Rsp packet
|
||||||
|
var b bytes.Buffer
|
||||||
|
if _, err := rp.WriteTo(&b); err != nil {
|
||||||
|
log.Printf("Couldn't create response packet: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, err := ln.WriteToUDP(b.Bytes(), raddr); err != nil {
|
||||||
|
log.Printf("Couldn't write response packet to UDP peer %v: %v", raddr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *aurp.OpenRspPacket:
|
||||||
|
if p.RateOrErrCode < 0 {
|
||||||
|
// It's an error code.
|
||||||
|
log.Printf("Open-Rsp error code from peer %v: %d", raddr.IP, p.RateOrErrCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue