Compare commits

...

2 Commits

Author SHA1 Message Date
Neil Alexander
2bc2b77ed9 Update ironwood dependency (temporary replace) 2024-07-24 18:28:08 +01:00
Neil Alexander
bfdcf0762f Link cost-aware routing 2024-07-24 18:28:08 +01:00
13 changed files with 60 additions and 8 deletions

View File

@@ -247,6 +247,7 @@ func main() {
Listen: intf.Listen, Listen: intf.Listen,
Port: intf.Port, Port: intf.Port,
Priority: uint8(intf.Priority), Priority: uint8(intf.Priority),
Cost: uint8(intf.Cost),
Password: intf.Password, Password: intf.Password,
}) })
} }

View File

@@ -174,7 +174,7 @@ func run() int {
if err := json.Unmarshal(recv.Response, &resp); err != nil { if err := json.Unmarshal(recv.Response, &resp); err != nil {
panic(err) panic(err)
} }
table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Last Error"}) table.SetHeader([]string{"URI", "State", "Dir", "IP Address", "Uptime", "RTT", "RX", "TX", "Pr", "Cost", "Last Error"})
for _, peer := range resp.Peers { for _, peer := range resp.Peers {
state, lasterr, dir, rtt := "Up", "-", "Out", "-" state, lasterr, dir, rtt := "Up", "-", "Out", "-"
if !peer.Up { if !peer.Up {
@@ -200,6 +200,7 @@ func run() int {
peer.RXBytes.String(), peer.RXBytes.String(),
peer.TXBytes.String(), peer.TXBytes.String(),
fmt.Sprintf("%d", peer.Priority), fmt.Sprintf("%d", peer.Priority),
fmt.Sprintf("%d", peer.Cost),
lasterr, lasterr,
}) })
} }

View File

@@ -97,6 +97,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
Listen: intf.Listen, Listen: intf.Listen,
Port: intf.Port, Port: intf.Port,
Priority: uint8(intf.Priority), Priority: uint8(intf.Priority),
Cost: uint8(intf.Cost),
Password: intf.Password, Password: intf.Password,
}) })
} }

2
go.mod
View File

@@ -2,6 +2,8 @@ module github.com/yggdrasil-network/yggdrasil-go
go 1.21 go 1.21
replace github.com/Arceliar/ironwood => github.com/neilalexander/ironwood v0.0.0-20240530214820-2be4c2c0545a
require ( require (
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d

4
go.sum
View File

@@ -1,5 +1,3 @@
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2 h1:SBdYBKeXYUUFef5wi2CMhYmXFVGiYaRpTvbki0Bu+JQ=
github.com/Arceliar/ironwood v0.0.0-20240529054413-b8e59574e2b2/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk=
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q= github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
@@ -48,6 +46,8 @@ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/neilalexander/ironwood v0.0.0-20240530214820-2be4c2c0545a h1:QtmEQn0ahcIFkkt8iXjBlyej984hdFS6Cc2cqTN2CuQ=
github.com/neilalexander/ironwood v0.0.0-20240530214820-2be4c2c0545a/go.mod h1:6WP4799FX0OuWdENGQAh+0RXp9FLh0y7NZ7tM9cJyXk=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=

View File

@@ -24,6 +24,7 @@ type PeerEntry struct {
PublicKey string `json:"key"` PublicKey string `json:"key"`
Port uint64 `json:"port"` Port uint64 `json:"port"`
Priority uint64 `json:"priority"` Priority uint64 `json:"priority"`
Cost uint64 `json:"cost"`
RXBytes DataUnit `json:"bytes_recvd,omitempty"` RXBytes DataUnit `json:"bytes_recvd,omitempty"`
TXBytes DataUnit `json:"bytes_sent,omitempty"` TXBytes DataUnit `json:"bytes_sent,omitempty"`
Uptime float64 `json:"uptime,omitempty"` Uptime float64 `json:"uptime,omitempty"`
@@ -41,6 +42,7 @@ func (a *AdminSocket) getPeersHandler(_ *GetPeersRequest, res *GetPeersResponse)
Up: p.Up, Up: p.Up,
Inbound: p.Inbound, Inbound: p.Inbound,
Priority: uint64(p.Priority), // can't be uint8 thanks to gobind Priority: uint64(p.Priority), // can't be uint8 thanks to gobind
Cost: uint64(p.Cost), // can't be uint8 thanks to gobind
URI: p.URI, URI: p.URI,
RXBytes: DataUnit(p.RXBytes), RXBytes: DataUnit(p.RXBytes),
TXBytes: DataUnit(p.TXBytes), TXBytes: DataUnit(p.TXBytes),

View File

@@ -62,6 +62,7 @@ type MulticastInterfaceConfig struct {
Listen bool Listen bool
Port uint16 Port uint16
Priority uint64 // really uint8, but gobind won't export it Priority uint64 // really uint8, but gobind won't export it
Cost uint16 // really uint8, but gobind won't export it
Password string Password string
} }

View File

@@ -30,6 +30,7 @@ type PeerInfo struct {
Coords []uint64 Coords []uint64
Port uint64 Port uint64
Priority uint8 Priority uint8
Cost uint8
RXBytes uint64 RXBytes uint64
TXBytes uint64 TXBytes uint64
Uptime time.Duration Uptime time.Duration
@@ -94,6 +95,7 @@ func (c *Core) GetPeers() []PeerInfo {
peerinfo.Port = p.Port peerinfo.Port = p.Port
peerinfo.Priority = p.Priority peerinfo.Priority = p.Priority
peerinfo.Latency = p.Latency peerinfo.Latency = p.Latency
peerinfo.Cost = uint8(p.Cost)
} }
peers = append(peers, peerinfo) peers = append(peers, peerinfo)
} }

View File

@@ -70,6 +70,7 @@ type link struct {
type linkOptions struct { type linkOptions struct {
pinnedEd25519Keys map[keyArray]struct{} pinnedEd25519Keys map[keyArray]struct{}
priority uint8 priority uint8
cost uint8
tlsSNI string tlsSNI string
password []byte password []byte
maxBackoff time.Duration maxBackoff time.Duration
@@ -139,6 +140,7 @@ func (e linkError) Error() string { return string(e) }
const ErrLinkAlreadyConfigured = linkError("peer is already configured") const ErrLinkAlreadyConfigured = linkError("peer is already configured")
const ErrLinkNotConfigured = linkError("peer is not configured") const ErrLinkNotConfigured = linkError("peer is not configured")
const ErrLinkPriorityInvalid = linkError("priority value is invalid") const ErrLinkPriorityInvalid = linkError("priority value is invalid")
const ErrLinkCostInvalid = linkError("cost value is invalid")
const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid") const ErrLinkPinnedKeyInvalid = linkError("pinned public key is invalid")
const ErrLinkPasswordInvalid = linkError("password is invalid") const ErrLinkPasswordInvalid = linkError("password is invalid")
const ErrLinkUnrecognisedSchema = linkError("link schema unknown") const ErrLinkUnrecognisedSchema = linkError("link schema unknown")
@@ -181,6 +183,14 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
} }
options.priority = uint8(pi) options.priority = uint8(pi)
} }
if p := u.Query().Get("cost"); p != "" {
c, err := strconv.ParseUint(p, 10, 8)
if err != nil {
retErr = ErrLinkCostInvalid
return
}
options.cost = uint8(c)
}
if p := u.Query().Get("password"); p != "" { if p := u.Query().Get("password"); p != "" {
if len(p) > blake2b.Size { if len(p) > blake2b.Size {
retErr = ErrLinkPasswordInvalid retErr = ErrLinkPasswordInvalid
@@ -448,6 +458,13 @@ func (l *links) listen(u *url.URL, sintf string) (*Listener, error) {
} }
options.priority = uint8(pi) options.priority = uint8(pi)
} }
if p := u.Query().Get("cost"); p != "" {
c, err := strconv.ParseUint(p, 10, 8)
if err != nil {
return nil, ErrLinkCostInvalid
}
options.cost = uint8(c)
}
if p := u.Query().Get("password"); p != "" { if p := u.Query().Get("password"); p != "" {
if len(p) > blake2b.Size { if len(p) > blake2b.Size {
return nil, ErrLinkPasswordInvalid return nil, ErrLinkPasswordInvalid
@@ -567,6 +584,7 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
meta := version_getBaseMetadata() meta := version_getBaseMetadata()
meta.publicKey = l.core.public meta.publicKey = l.core.public
meta.priority = options.priority meta.priority = options.priority
meta.cost = options.cost
metaBytes, err := meta.encode(l.core.secret, options.password) metaBytes, err := meta.encode(l.core.secret, options.password)
if err != nil { if err != nil {
return fmt.Errorf("failed to generate handshake: %w", err) return fmt.Errorf("failed to generate handshake: %w", err)
@@ -628,17 +646,20 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn, s
remoteAddr := net.IP(address.AddrForKey(meta.publicKey)[:]).String() remoteAddr := net.IP(address.AddrForKey(meta.publicKey)[:]).String()
remoteStr := fmt.Sprintf("%s@%s", remoteAddr, conn.RemoteAddr()) remoteStr := fmt.Sprintf("%s@%s", remoteAddr, conn.RemoteAddr())
localStr := conn.LocalAddr() localStr := conn.LocalAddr()
priority := options.priority cost, priority := options.cost, options.priority
if meta.priority > priority { if meta.priority > priority {
priority = meta.priority priority = meta.priority
} }
if meta.cost > cost {
cost = meta.cost
}
l.core.log.Infof("Connected %s: %s, source %s", l.core.log.Infof("Connected %s: %s, source %s",
dir, remoteStr, localStr) dir, remoteStr, localStr)
if success != nil { if success != nil {
success() success()
} }
err = l.core.HandleConn(meta.publicKey, conn, priority) err = l.core.HandleConn(meta.publicKey, conn, cost, priority)
switch err { switch err {
case io.EOF, net.ErrClosed, nil: case io.EOF, net.ErrClosed, nil:
l.core.log.Infof("Disconnected %s: %s, source %s", l.core.log.Infof("Disconnected %s: %s, source %s",

View File

@@ -22,6 +22,7 @@ type version_metadata struct {
minorVer uint16 minorVer uint16
publicKey ed25519.PublicKey publicKey ed25519.PublicKey
priority uint8 priority uint8
cost uint8
} }
const ( const (
@@ -36,6 +37,7 @@ const (
metaVersionMinor // uint16 metaVersionMinor // uint16
metaPublicKey // [32]byte metaPublicKey // [32]byte
metaPriority // uint8 metaPriority // uint8
metaCost // uint8
) )
// Gets a base metadata with no keys set, but with the correct version numbers. // Gets a base metadata with no keys set, but with the correct version numbers.
@@ -64,9 +66,17 @@ func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte
bs = binary.BigEndian.AppendUint16(bs, ed25519.PublicKeySize) bs = binary.BigEndian.AppendUint16(bs, ed25519.PublicKeySize)
bs = append(bs, m.publicKey[:]...) bs = append(bs, m.publicKey[:]...)
bs = binary.BigEndian.AppendUint16(bs, metaPriority) if m.priority > 0 {
bs = binary.BigEndian.AppendUint16(bs, 1) bs = binary.BigEndian.AppendUint16(bs, metaPriority)
bs = append(bs, m.priority) bs = binary.BigEndian.AppendUint16(bs, 1)
bs = append(bs, m.priority)
}
if m.cost > 0 {
bs = binary.BigEndian.AppendUint16(bs, metaCost)
bs = binary.BigEndian.AppendUint16(bs, 1)
bs = append(bs, m.cost)
}
hasher, err := blake2b.New512(password) hasher, err := blake2b.New512(password)
if err != nil { if err != nil {
@@ -126,6 +136,9 @@ func (m *version_metadata) decode(r io.Reader, password []byte) error {
case metaPriority: case metaPriority:
m.priority = bs[0] m.priority = bs[0]
case metaCost:
m.cost = bs[0]
} }
bs = bs[oplen:] bs = bs[oplen:]
} }

View File

@@ -52,6 +52,9 @@ func TestVersionRoundtrip(t *testing.T) {
{majorVer: 258, minorVer: 259}, {majorVer: 258, minorVer: 259},
{majorVer: 3, minorVer: 5, priority: 6}, {majorVer: 3, minorVer: 5, priority: 6},
{majorVer: 260, minorVer: 261, priority: 7}, {majorVer: 260, minorVer: 261, priority: 7},
{majorVer: 258, minorVer: 259, cost: 5},
{majorVer: 3, minorVer: 5, priority: 6, cost: 12},
{majorVer: 260, minorVer: 261, priority: 7, cost: 1},
} { } {
// Generate a random public key for each time, since it is // Generate a random public key for each time, since it is
// a required field. // a required field.

View File

@@ -45,6 +45,7 @@ type interfaceInfo struct {
listen bool listen bool
port uint16 port uint16
priority uint8 priority uint8
cost uint8
password []byte password []byte
hash []byte hash []byte
} }
@@ -214,6 +215,7 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
listen: ifcfg.Listen, listen: ifcfg.Listen,
port: ifcfg.Port, port: ifcfg.Port,
priority: ifcfg.Priority, priority: ifcfg.Priority,
cost: ifcfg.Cost,
password: []byte(ifcfg.Password), password: []byte(ifcfg.Password),
hash: hasher.Sum(nil), hash: hasher.Sum(nil),
} }
@@ -314,6 +316,7 @@ func (m *Multicast) _announce() {
// No listener was found - let's create one // No listener was found - let's create one
v := &url.Values{} v := &url.Values{}
v.Add("priority", fmt.Sprintf("%d", info.priority)) v.Add("priority", fmt.Sprintf("%d", info.priority))
v.Add("cost", fmt.Sprintf("%d", info.cost))
v.Add("password", string(info.password)) v.Add("password", string(info.password))
u := &url.URL{ u := &url.URL{
Scheme: "tls", Scheme: "tls",
@@ -428,6 +431,7 @@ func (m *Multicast) listen() {
v := &url.Values{} v := &url.Values{}
v.Add("key", hex.EncodeToString(adv.PublicKey)) v.Add("key", hex.EncodeToString(adv.PublicKey))
v.Add("priority", fmt.Sprintf("%d", info.priority)) v.Add("priority", fmt.Sprintf("%d", info.priority))
v.Add("cost", fmt.Sprintf("%d", info.cost))
v.Add("password", string(info.password)) v.Add("password", string(info.password))
u := &url.URL{ u := &url.URL{
Scheme: "tls", Scheme: "tls",

View File

@@ -21,6 +21,7 @@ type MulticastInterface struct {
Listen bool Listen bool
Port uint16 Port uint16
Priority uint8 Priority uint8
Cost uint8
Password string Password string
} }