jrouter/main.go

266 lines
5.9 KiB
Go
Raw Normal View History

2024-03-10 11:57:03 +11:00
package main
2024-03-15 15:17:21 +11:00
import (
2024-03-22 16:14:55 +11:00
"bytes"
"flag"
2024-03-15 15:17:21 +11:00
"log"
"net"
2024-03-24 18:01:24 +11:00
"regexp"
2024-03-15 15:17:21 +11:00
"gitea.drjosh.dev/josh/jrouter/aurp"
)
2024-03-24 18:01:24 +11:00
var hasPortRE = regexp.MustCompile(`:\d+$`)
var configFilePath = flag.String("config", "jrouter.yaml", "Path to configuration file to use")
2024-03-10 11:57:03 +11:00
func main() {
2024-03-22 16:14:55 +11:00
flag.Parse()
2024-03-15 15:17:21 +11:00
log.Println("jrouter")
2024-03-24 18:01:24 +11:00
cfg, err := loadConfig(*configFilePath)
if err != nil {
log.Fatalf("Couldn't load configuration file: %v", err)
}
localIP := net.ParseIP(cfg.LocalIP).To4()
2024-03-22 16:14:55 +11:00
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 {
2024-03-24 18:01:24 +11:00
log.Fatalf("No global unicast IPv4 addresses on any network interfaces, and no valid local_ip address in configuration")
2024-03-22 16:14:55 +11:00
}
}
2024-03-24 21:10:24 +11:00
localDI := aurp.IPDomainIdentifier(localIP)
2024-03-22 16:14:55 +11:00
log.Printf("Using %v as local domain identifier", localIP)
2024-03-24 18:01:24 +11:00
peers := make(map[udpAddr]*peer)
2024-03-22 16:14:55 +11:00
var nextConnID uint16
2024-03-24 18:01:24 +11:00
ln, err := net.ListenUDP("udp4", &net.UDPAddr{Port: int(cfg.ListenPort)})
2024-03-15 15:17:21 +11:00
if err != nil {
log.Fatalf("Couldn't listen on udp4:387: %v", err)
}
2024-03-24 18:01:24 +11:00
defer ln.Close()
log.Printf("Listening on %v", ln.LocalAddr())
for _, peerStr := range cfg.Peers {
if !hasPortRE.MatchString(peerStr) {
peerStr += ":387"
}
raddr, err := net.ResolveUDPAddr("udp4", peerStr)
if err != nil {
log.Fatalf("Invalid UDP address: %v", err)
}
log.Printf("resolved %q to %v", peerStr, raddr)
tr := &aurp.Transport{
2024-03-24 21:10:24 +11:00
LocalDI: localDI,
2024-03-24 18:01:24 +11:00
RemoteDI: aurp.IPDomainIdentifier(raddr.IP),
LocalConnID: nextConnID,
}
nextConnID++
peer := &peer{
tr: tr,
conn: ln,
raddr: raddr,
2024-03-24 21:10:24 +11:00
recv: make(chan aurp.Packet, 1024),
2024-03-24 18:01:24 +11:00
}
2024-03-24 21:10:24 +11:00
go peer.handle()
2024-03-24 18:01:24 +11:00
peers[udpAddrFromNet(raddr)] = peer
}
2024-03-15 15:17:21 +11:00
// Incoming packet loop
pb := make([]byte, 65536)
for {
2024-03-22 16:14:55 +11:00
pktlen, raddr, readErr := ln.ReadFromUDP(pb)
// net.PacketConn.ReadFrom: "Callers should always process
2024-03-15 16:15:24 +11:00
// the n > 0 bytes returned before considering the error err."
2024-03-24 18:01:24 +11:00
log.Printf("Received packet of length %d from %v", pktlen, raddr)
2024-03-24 21:31:11 +11:00
dh, pkt, parseErr := aurp.ParsePacket(pb[:pktlen])
2024-03-15 16:15:24 +11:00
if parseErr != nil {
log.Printf("Failed to parse packet: %v", parseErr)
2024-03-24 21:10:24 +11:00
continue
2024-03-15 15:17:21 +11:00
}
2024-03-24 18:01:24 +11:00
log.Printf("The packet parsed succesfully as a %T", pkt)
2024-03-15 16:15:24 +11:00
if readErr != nil {
log.Printf("Failed to read packet: %v", readErr)
continue
2024-03-15 15:17:21 +11:00
}
2024-03-22 16:14:55 +11:00
// Existing peer?
2024-03-24 18:01:24 +11:00
ra := udpAddrFromNet(raddr)
pr := peers[ra]
if pr == nil {
2024-03-22 16:14:55 +11:00
// New peer!
nextConnID++
2024-03-24 18:01:24 +11:00
pr = &peer{
tr: &aurp.Transport{
2024-03-24 21:10:24 +11:00
LocalDI: localDI,
2024-03-24 18:01:24 +11:00
RemoteDI: dh.SourceDI, // platinum rule
LocalConnID: nextConnID,
},
conn: ln,
raddr: raddr,
2024-03-24 21:10:24 +11:00
recv: make(chan aurp.Packet, 1024),
2024-03-24 18:01:24 +11:00
}
peers[ra] = pr
2024-03-24 21:10:24 +11:00
go pr.handle()
2024-03-22 16:14:55 +11:00
}
2024-03-24 21:10:24 +11:00
// Pass the packet to the goroutine in charge of this peer.
pr.recv <- pkt
}
}
type peer struct {
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
}
return p.conn.WriteToUDP(b.Bytes(), p.raddr)
}
func (p *peer) handle() {
// Write an Open-Req packet
n, err := p.send(p.tr.NewOpenReqPacket(nil))
if err != nil {
log.Printf("Couldn't send Open-Req packet: %v", err)
return
}
log.Printf("Sent Open-Req (len %d) to peer %v", n, p.raddr)
for pkt := range p.recv {
switch pkt := pkt.(type) {
2024-03-22 16:14:55 +11:00
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.
2024-03-24 21:10:24 +11:00
p.tr.RemoteConnID = pkt.ConnectionID
2024-03-22 16:14:55 +11:00
// Formulate a response.
2024-03-24 21:10:24 +11:00
var orsp *aurp.OpenRspPacket
2024-03-22 16:18:18 +11:00
switch {
2024-03-24 21:10:24 +11:00
case pkt.Version != 1:
2024-03-22 16:14:55 +11:00
// Respond with Open-Rsp with unknown version error.
2024-03-24 21:10:24 +11:00
orsp = p.tr.NewOpenRspPacket(0, aurp.ErrCodeInvalidVersion, nil)
2024-03-22 16:18:18 +11:00
2024-03-24 21:10:24 +11:00
case len(pkt.Options) > 0:
2024-03-22 16:18:18 +11:00
// Options? OPTIONS? We don't accept no stinkin' _options_
2024-03-24 21:10:24 +11:00
orsp = p.tr.NewOpenRspPacket(0, aurp.ErrCodeOptionNegotiation, nil)
2024-03-22 16:18:18 +11:00
default:
// Accept it I guess.
2024-03-24 21:10:24 +11:00
orsp = p.tr.NewOpenRspPacket(0, 1, nil)
2024-03-22 16:14:55 +11:00
}
2024-03-24 21:10:24 +11:00
log.Printf("Responding with %T", orsp)
2024-03-24 18:01:24 +11:00
2024-03-24 21:10:24 +11:00
if _, err := p.send(orsp); err != nil {
log.Printf("Couldn't send Open-Rsp: %v", err)
2024-03-22 16:14:55 +11:00
}
case *aurp.OpenRspPacket:
2024-03-24 21:10:24 +11:00
if pkt.RateOrErrCode < 0 {
2024-03-22 16:14:55 +11:00
// It's an error code.
2024-03-24 21:10:24 +11:00
log.Printf("Open-Rsp error code from peer %v: %d", p.raddr.IP, pkt.RateOrErrCode)
// Close the connection
2024-03-22 16:14:55 +11:00
}
2024-03-24 21:24:53 +11:00
// 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
2024-03-22 16:14:55 +11:00
}
2024-03-15 15:17:21 +11:00
}
2024-03-10 11:57:03 +11:00
}
2024-03-24 18:01:24 +11:00
// Hashable net.UDPAddr
type udpAddr struct {
ipv4 [4]byte
port uint16
}
func udpAddrFromNet(a *net.UDPAddr) udpAddr {
return udpAddr{
ipv4: [4]byte(a.IP.To4()),
port: uint16(a.Port),
}
}
func (u udpAddr) toNet() *net.UDPAddr {
return &net.UDPAddr{
IP: u.ipv4[:],
Port: int(u.port),
}
}