5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-25 06:01:35 +00:00

Version 0.4.6

This commit is contained in:
Neil Alexander 2022-10-26 18:25:48 +01:00
parent b8a2d9f125
commit 4c66a13b93
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
17 changed files with 174 additions and 115 deletions

View File

@ -26,6 +26,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- in case of vulnerabilities. - in case of vulnerabilities.
--> -->
## [0.4.6] - 2022-10-25
### Added
- Support for prioritising multiple peerings to the same node has been added, useful for nodes with multiple network interfaces
- The priority can be configured by specifying `?priority=X` in a `Peers` or `Listen` URI, or by specifying `Priority` within a `MulticastInterfaces` configuration entry
- Priorities are values between 0 and 254 (default is 0), lower numbers are prioritised and nodes will automatically negotiate the higher of the two values
### Changed
- On Linux, `SO_REUSEADDR` is now used on the multicast port instead of `SO_REUSEPORT`, which should allow processes running under different users to run simultaneously
### Fixed
- Adding peers using the `InterfacePeers` configuration option should now work correctly again
- Multiple connections from the same remote IP address will no longer be incorrectly dropped
- The admin socket will no longer incorrectly claim TCP connections as TLS
- A panic that could occur when calling `GetPeers` while a peering link is being set up has been fixed
## [0.4.5] - 2022-10-15 ## [0.4.5] - 2022-10-15
### Added ### Added

View File

@ -335,10 +335,11 @@ func run(args yggArgs, ctx context.Context) {
options := []multicast.SetupOption{} options := []multicast.SetupOption{}
for _, intf := range cfg.MulticastInterfaces { for _, intf := range cfg.MulticastInterfaces {
options = append(options, multicast.MulticastInterface{ options = append(options, multicast.MulticastInterface{
Regex: regexp.MustCompile(intf.Regex), Regex: regexp.MustCompile(intf.Regex),
Beacon: intf.Beacon, Beacon: intf.Beacon,
Listen: intf.Listen, Listen: intf.Listen,
Port: intf.Port, Port: intf.Port,
Priority: intf.Priority,
}) })
} }
if n.multicast, err = multicast.New(n.core, logger, options...); err != nil { if n.multicast, err = multicast.New(n.core, logger, options...); err != nil {

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{"Port", "Public Key", "IP Address", "Uptime", "RX", "TX", "URI"}) table.SetHeader([]string{"Port", "Public Key", "IP Address", "Uptime", "RX", "TX", "Pr", "URI"})
for _, peer := range resp.Peers { for _, peer := range resp.Peers {
table.Append([]string{ table.Append([]string{
fmt.Sprintf("%d", peer.Port), fmt.Sprintf("%d", peer.Port),
@ -183,6 +183,7 @@ func run() int {
(time.Duration(peer.Uptime) * time.Second).String(), (time.Duration(peer.Uptime) * time.Second).String(),
peer.RXBytes.String(), peer.RXBytes.String(),
peer.TXBytes.String(), peer.TXBytes.String(),
fmt.Sprintf("%d", peer.Priority),
peer.Remote, peer.Remote,
}) })
} }

View File

@ -83,10 +83,11 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
options := []multicast.SetupOption{} options := []multicast.SetupOption{}
for _, intf := range m.config.MulticastInterfaces { for _, intf := range m.config.MulticastInterfaces {
options = append(options, multicast.MulticastInterface{ options = append(options, multicast.MulticastInterface{
Regex: regexp.MustCompile(intf.Regex), Regex: regexp.MustCompile(intf.Regex),
Beacon: intf.Beacon, Beacon: intf.Beacon,
Listen: intf.Listen, Listen: intf.Listen,
Port: intf.Port, Port: intf.Port,
Priority: intf.Priority,
}) })
} }
m.multicast, err = multicast.New(m.core, logger, options...) m.multicast, err = multicast.New(m.core, logger, options...)

2
go.mod
View File

@ -3,7 +3,7 @@ module github.com/yggdrasil-network/yggdrasil-go
go 1.17 go 1.17
require ( require (
github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6 github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979
github.com/cheggaaa/pb/v3 v3.0.8 github.com/cheggaaa/pb/v3 v3.0.8
github.com/gologme/log v1.2.0 github.com/gologme/log v1.2.0

15
go.sum
View File

@ -1,5 +1,5 @@
github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6 h1:iwL6nm2ibyuHXYimRNtFof7RJfe8JB+6CPDskV7K7gA= github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2 h1:Usab30pNT2i/vZvpXcN9uOr5IO1RZPcUqoGH0DIAPnU=
github.com/Arceliar/ironwood v0.0.0-20220924160422-ed4b6d4750b6/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk= github.com/Arceliar/ironwood v0.0.0-20221025225125-45b4281814c2/go.mod h1:RP72rucOFm5udrnEzTmIWLRVGQiV/fSUAQXJ0RST/nk=
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3ugUxx2EnnWmgba1kCqPkd4Gk1yQ=
github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
@ -17,8 +17,6 @@ github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwM
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw= github.com/hjson/hjson-go v3.1.0+incompatible h1:DY/9yE8ey8Zv22bY+mHV1uk2yRy0h8tKhZ77hEdi0Aw=
github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/hjson/hjson-go v3.1.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
github.com/kardianos/minwinsvc v1.0.0 h1:+JfAi8IBJna0jY2dJGZqi7o15z13JelFIklJCAENALA=
github.com/kardianos/minwinsvc v1.0.0/go.mod h1:Bgd0oc+D0Qo3bBytmNtyRKVlp85dAloLKhfxanPFFRc=
github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0= github.com/kardianos/minwinsvc v1.0.2 h1:JmZKFJQrmTGa/WiW+vkJXKmfzdjabuEW4Tirj5lLdR0=
github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4= github.com/kardianos/minwinsvc v1.0.2/go.mod h1:LUZNYhNmxujx2tR7FbdxqYJ9XDDoCd3MQcl1o//FWl4=
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
@ -50,7 +48,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a h1:NmSIgad6KjE6VvHciPZuNRTKxGhlPfD6OA87W/PLkqg=
golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20221012134737-56aed061732a/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@ -58,8 +55,6 @@ golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9t
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105 h1:3vUV5x5+3LfQbgk7paCM6INOaJG9xXQbn79xoNkwfIk=
golang.org/x/mobile v0.0.0-20220722155234-aaac322e2105/go.mod h1:pe2sM7Uk+2Su1y7u/6Z8KJ24D7lepUjFZbhFOrmDfuQ=
golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 h1:K4fp1rDuJBz0FCPAWzIJwnzwNEM7S6yobdZzMrZ/Zws= golang.org/x/mobile v0.0.0-20221012134814-c746ac228303 h1:K4fp1rDuJBz0FCPAWzIJwnzwNEM7S6yobdZzMrZ/Zws=
golang.org/x/mobile v0.0.0-20221012134814-c746ac228303/go.mod h1:M32cGdzp91A8Ex9qQtyZinr19EYxzkFqDjW2oyHzTDQ= golang.org/x/mobile v0.0.0-20221012134814-c746ac228303/go.mod h1:M32cGdzp91A8Ex9qQtyZinr19EYxzkFqDjW2oyHzTDQ=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@ -73,7 +68,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211011170408-caeb26a5c8c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b h1:tvrvnPFcdzp294diPnrdZZZ8XUt2Tyj7svb7X52iDuU=
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
@ -87,7 +82,6 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -97,9 +91,7 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 h1:OK7RB6t2WQX54srQQYSXMW8dF5C6/8+oA/s5QBmmto4=
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -109,7 +101,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b h1:NXqSWXSRUSCaFuvitrWtU169I3876zRTalMRbfd6LL0=
golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= golang.org/x/text v0.3.8-0.20211004125949-5bd84dd9b33b/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=

View File

@ -19,6 +19,7 @@ type PeerEntry struct {
IPAddress string `json:"address"` IPAddress string `json:"address"`
PublicKey string `json:"key"` PublicKey string `json:"key"`
Port uint64 `json:"port"` Port uint64 `json:"port"`
Priority uint8 `json:"priority"`
Coords []uint64 `json:"coords"` Coords []uint64 `json:"coords"`
Remote string `json:"remote"` Remote string `json:"remote"`
RXBytes DataUnit `json:"bytes_recvd"` RXBytes DataUnit `json:"bytes_recvd"`
@ -35,6 +36,7 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons
IPAddress: net.IP(addr[:]).String(), IPAddress: net.IP(addr[:]).String(),
PublicKey: hex.EncodeToString(p.Key), PublicKey: hex.EncodeToString(p.Key),
Port: p.Port, Port: p.Port,
Priority: p.Priority,
Coords: p.Coords, Coords: p.Coords,
Remote: p.Remote, Remote: p.Remote,
RXBytes: DataUnit(p.RXBytes), RXBytes: DataUnit(p.RXBytes),
@ -43,6 +45,9 @@ func (a *AdminSocket) getPeersHandler(req *GetPeersRequest, res *GetPeersRespons
}) })
} }
sort.Slice(res.Peers, func(i, j int) bool { sort.Slice(res.Peers, func(i, j int) bool {
if res.Peers[i].Port == res.Peers[j].Port {
return res.Peers[i].Priority < res.Peers[j].Priority
}
return res.Peers[i].Port < res.Peers[j].Port return res.Peers[i].Port < res.Peers[j].Port
}) })
return nil return nil

View File

@ -40,10 +40,11 @@ type NodeConfig struct {
} }
type MulticastInterfaceConfig struct { type MulticastInterfaceConfig struct {
Regex string Regex string
Beacon bool Beacon bool
Listen bool Listen bool
Port uint16 Port uint16
Priority uint8
} }
// NewSigningKeys replaces the signing keypair in the NodeConfig with a new // NewSigningKeys replaces the signing keypair in the NodeConfig with a new

View File

@ -20,14 +20,15 @@ type SelfInfo struct {
} }
type PeerInfo struct { type PeerInfo struct {
Key ed25519.PublicKey Key ed25519.PublicKey
Root ed25519.PublicKey Root ed25519.PublicKey
Coords []uint64 Coords []uint64
Port uint64 Port uint64
Remote string Priority uint8
RXBytes uint64 Remote string
TXBytes uint64 RXBytes uint64
Uptime time.Duration TXBytes uint64
Uptime time.Duration
} }
type DHTEntryInfo struct { type DHTEntryInfo struct {
@ -62,6 +63,9 @@ func (c *Core) GetPeers() []PeerInfo {
names := make(map[net.Conn]string) names := make(map[net.Conn]string)
phony.Block(&c.links, func() { phony.Block(&c.links, func() {
for _, info := range c.links._links { for _, info := range c.links._links {
if info == nil {
continue
}
names[info.conn] = info.lname names[info.conn] = info.lname
} }
}) })
@ -72,6 +76,7 @@ func (c *Core) GetPeers() []PeerInfo {
info.Root = p.Root info.Root = p.Root
info.Coords = p.Coords info.Coords = p.Coords
info.Port = p.Port info.Port = p.Port
info.Priority = p.Priority
info.Remote = p.Conn.RemoteAddr().String() info.Remote = p.Conn.RemoteAddr().String()
if name := names[p.Conn]; name != "" { if name := names[p.Conn]; name != "" {
info.Remote = name info.Remote = name

View File

@ -8,6 +8,7 @@ import (
"io" "io"
"net" "net"
"net/url" "net/url"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
"time" "time"
@ -45,6 +46,7 @@ type link struct {
type linkOptions struct { type linkOptions struct {
pinnedEd25519Keys map[keyArray]struct{} pinnedEd25519Keys map[keyArray]struct{}
priority uint8
} }
type Listener struct { type Listener struct {
@ -120,17 +122,24 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) {
copy(sigPubKey[:], sigPub) copy(sigPubKey[:], sigPub)
options.pinnedEd25519Keys[sigPubKey] = struct{}{} options.pinnedEd25519Keys[sigPubKey] = struct{}{}
} }
if p := u.Query().Get("priority"); p != "" {
pi, err := strconv.ParseUint(p, 10, 8)
if err != nil {
return info, fmt.Errorf("priority invalid: %w", err)
}
options.priority = uint8(pi)
}
switch info.linkType { switch info.linkType {
case "tcp": case "tcp":
go func() { go func() {
if err := l.tcp.dial(u, options, sintf); err != nil { if err := l.tcp.dial(u, options, sintf); err != nil && err != io.EOF {
l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err) l.core.log.Warnf("Failed to dial TCP %s: %s\n", u.Host, err)
} }
}() }()
case "socks": case "socks":
go func() { go func() {
if err := l.socks.dial(u, options); err != nil { if err := l.socks.dial(u, options); err != nil && err != io.EOF {
l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err) l.core.log.Warnf("Failed to dial SOCKS %s: %s\n", u.Host, err)
} }
}() }()
@ -154,14 +163,14 @@ func (l *links) call(u *url.URL, sintf string) (linkInfo, error) {
} }
} }
go func() { go func() {
if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil { if err := l.tls.dial(u, options, sintf, tlsSNI); err != nil && err != io.EOF {
l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err) l.core.log.Warnf("Failed to dial TLS %s: %s\n", u.Host, err)
} }
}() }()
case "unix": case "unix":
go func() { go func() {
if err := l.unix.dial(u, options, sintf); err != nil { if err := l.unix.dial(u, options, sintf); err != nil && err != io.EOF {
l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err) l.core.log.Warnf("Failed to dial UNIX %s: %s\n", u.Host, err)
} }
}() }()
@ -272,8 +281,7 @@ func (intf *link) handler() error {
var key keyArray var key keyArray
copy(key[:], meta.key) copy(key[:], meta.key)
if _, allowed := pinned[key]; !allowed { if _, allowed := pinned[key]; !allowed {
intf.links.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name()) return fmt.Errorf("node public key that does not match pinned keys")
return fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys")
} }
} }
// Check if we're authorized to connect to this key / IP // Check if we're authorized to connect to this key / IP
@ -286,31 +294,33 @@ func (intf *link) handler() error {
} }
} }
if intf.incoming && !intf.force && !isallowed { if intf.incoming && !intf.force && !isallowed {
intf.links.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s",
strings.ToUpper(intf.info.linkType), intf.info.remote, hex.EncodeToString(meta.key))
_ = intf.close() _ = intf.close()
return fmt.Errorf("forbidden connection") return fmt.Errorf("node public key %q is not in AllowedPublicKeys", hex.EncodeToString(meta.key))
} }
phony.Block(intf.links, func() { phony.Block(intf.links, func() {
intf.links._links[intf.info] = intf intf.links._links[intf.info] = intf
}) })
dir := "outbound"
if intf.incoming {
dir = "inbound"
}
remoteAddr := net.IP(address.AddrForKey(meta.key)[:]).String() remoteAddr := net.IP(address.AddrForKey(meta.key)[:]).String()
remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote) remoteStr := fmt.Sprintf("%s@%s", remoteAddr, intf.info.remote)
localStr := intf.conn.LocalAddr() localStr := intf.conn.LocalAddr()
intf.links.core.log.Infof("Connected %s: %s, source %s", intf.links.core.log.Infof("Connected %s %s: %s, source %s",
strings.ToUpper(intf.info.linkType), remoteStr, localStr) dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr)
// TODO don't report an error if it's just a 'use of closed network connection' err = intf.links.core.HandleConn(meta.key, intf.conn, intf.options.priority)
if err = intf.links.core.HandleConn(meta.key, intf.conn); err != nil && err != io.EOF { switch err {
intf.links.core.log.Infof("Disconnected %s: %s, source %s; error: %s", case io.EOF, net.ErrClosed, nil:
strings.ToUpper(intf.info.linkType), remoteStr, localStr, err) intf.links.core.log.Infof("Disconnected %s %s: %s, source %s",
} else { dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr)
intf.links.core.log.Infof("Disconnected %s: %s, source %s", default:
strings.ToUpper(intf.info.linkType), remoteStr, localStr) intf.links.core.log.Infof("Disconnected %s %s: %s, source %s; error: %s",
dir, strings.ToUpper(intf.info.linkType), remoteStr, localStr, err)
} }
return nil return nil
} }
@ -318,14 +328,7 @@ func (intf *link) close() error {
return intf.conn.Close() return intf.conn.Close()
} }
func (intf *link) name() string {
return intf.lname
}
func linkInfoFor(linkType, sintf, remote string) linkInfo { func linkInfoFor(linkType, sintf, remote string) linkInfo {
if h, _, err := net.SplitHostPort(remote); err == nil {
remote = h
}
return linkInfo{ return linkInfo{
linkType: linkType, linkType: linkType,
local: sintf, local: sintf,
@ -353,3 +356,12 @@ func (c *linkConn) Write(p []byte) (n int, err error) {
atomic.AddUint64(&c.tx, uint64(n)) atomic.AddUint64(&c.tx, uint64(n))
return return
} }
func linkOptionsForListener(u *url.URL) (l linkOptions) {
if p := u.Query().Get("priority"); p != "" {
if pi, err := strconv.ParseUint(p, 10, 8); err == nil {
l.priority = uint8(pi)
}
}
return
}

View File

@ -23,7 +23,7 @@ func (l *links) newLinkSOCKS() *linkSOCKS {
func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error { func (l *linkSOCKS) dial(url *url.URL, options linkOptions) error {
info := linkInfoFor("socks", "", url.Path) info := linkInfoFor("socks", "", url.Path)
if l.links.isConnectedTo(info) { if l.links.isConnectedTo(info) {
return fmt.Errorf("duplicate connection attempt") return nil
} }
proxyAuth := &proxy.Auth{} proxyAuth := &proxy.Auth{}
proxyAuth.User = url.User.Username() proxyAuth.User = url.User.Username()

View File

@ -31,24 +31,24 @@ func (l *links) newLinkTCP() *linkTCP {
} }
func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error { func (l *linkTCP) dial(url *url.URL, options linkOptions, sintf string) error {
info := linkInfoFor("tcp", sintf, strings.SplitN(url.Host, "%", 2)[0])
if l.links.isConnectedTo(info) {
return fmt.Errorf("duplicate connection attempt")
}
addr, err := net.ResolveTCPAddr("tcp", url.Host) addr, err := net.ResolveTCPAddr("tcp", url.Host)
if err != nil { if err != nil {
return err return err
} }
addr.Zone = sintf dialer, err := l.dialerFor(addr, sintf)
dialer, err := l.dialerFor(addr.String(), sintf)
if err != nil { if err != nil {
return err return err
} }
info := linkInfoFor("tcp", sintf, tcpIDFor(dialer.LocalAddr, addr))
if l.links.isConnectedTo(info) {
return nil
}
conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String()) conn, err := dialer.DialContext(l.core.ctx, "tcp", addr.String())
if err != nil { if err != nil {
return err return err
} }
return l.handler(url.String(), info, conn, options, false) uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/")
return l.handler(uri, info, conn, options, false, false)
} }
func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) { func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) {
@ -82,10 +82,11 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) {
cancel() cancel()
break break
} }
addr := conn.RemoteAddr().(*net.TCPAddr) laddr := conn.LocalAddr().(*net.TCPAddr)
name := fmt.Sprintf("tls://%s", addr) raddr := conn.RemoteAddr().(*net.TCPAddr)
info := linkInfoFor("tcp", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) name := fmt.Sprintf("tcp://%s", raddr)
if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { info := linkInfoFor("tcp", sintf, tcpIDFor(laddr, raddr))
if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil {
l.core.log.Errorln("Failed to create inbound link:", err) l.core.log.Errorln("Failed to create inbound link:", err)
} }
} }
@ -96,13 +97,13 @@ func (l *linkTCP) listen(url *url.URL, sintf string) (*Listener, error) {
return entry, nil return entry, nil
} }
func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { func (l *linkTCP) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error {
return l.links.create( return l.links.create(
conn, // connection conn, // connection
name, // connection name name, // connection name
info, // connection info info, // connection info
incoming, // not incoming incoming, // not incoming
false, // not forced force, // not forced
options, // connection options options, // connection options
) )
} }
@ -121,13 +122,11 @@ func (l *linkTCP) getAddr() *net.TCPAddr {
return addr return addr
} }
func (l *linkTCP) dialerFor(saddr, sintf string) (*net.Dialer, error) { func (l *linkTCP) dialerFor(dst *net.TCPAddr, sintf string) (*net.Dialer, error) {
dst, err := net.ResolveTCPAddr("tcp", saddr)
if err != nil {
return nil, err
}
if dst.IP.IsLinkLocalUnicast() { if dst.IP.IsLinkLocalUnicast() {
dst.Zone = sintf if sintf != "" {
dst.Zone = sintf
}
if dst.Zone == "" { if dst.Zone == "" {
return nil, fmt.Errorf("link-local address requires a zone") return nil, fmt.Errorf("link-local address requires a zone")
} }
@ -181,3 +180,16 @@ func (l *linkTCP) dialerFor(saddr, sintf string) (*net.Dialer, error) {
} }
return dialer, nil return dialer, nil
} }
func tcpIDFor(local net.Addr, remoteAddr *net.TCPAddr) string {
if localAddr, ok := local.(*net.TCPAddr); ok && localAddr.IP.Equal(remoteAddr.IP) {
// Nodes running on the same host — include both the IP and port.
return remoteAddr.String()
}
if remoteAddr.IP.IsLinkLocalUnicast() {
// Nodes discovered via multicast — include the IP only.
return remoteAddr.IP.String()
}
// Nodes connected remotely — include both the IP and port.
return remoteAddr.String()
}

View File

@ -47,19 +47,18 @@ func (l *links) newLinkTLS(tcp *linkTCP) *linkTLS {
} }
func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error { func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) error {
info := linkInfoFor("tls", sintf, strings.SplitN(url.Host, "%", 2)[0])
if l.links.isConnectedTo(info) {
return fmt.Errorf("duplicate connection attempt")
}
addr, err := net.ResolveTCPAddr("tcp", url.Host) addr, err := net.ResolveTCPAddr("tcp", url.Host)
if err != nil { if err != nil {
return err return err
} }
addr.Zone = sintf dialer, err := l.tcp.dialerFor(addr, sintf)
dialer, err := l.tcp.dialerFor(addr.String(), sintf)
if err != nil { if err != nil {
return err return err
} }
info := linkInfoFor("tls", sintf, tcpIDFor(dialer.LocalAddr, addr))
if l.links.isConnectedTo(info) {
return nil
}
tlsconfig := l.config.Clone() tlsconfig := l.config.Clone()
tlsconfig.ServerName = sni tlsconfig.ServerName = sni
tlsdialer := &tls.Dialer{ tlsdialer := &tls.Dialer{
@ -70,7 +69,8 @@ func (l *linkTLS) dial(url *url.URL, options linkOptions, sintf, sni string) err
if err != nil { if err != nil {
return err return err
} }
return l.handler(url.String(), info, conn, options, false) uri := strings.TrimRight(strings.SplitN(url.String(), "?", 2)[0], "/")
return l.handler(uri, info, conn, options, false, false)
} }
func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) { func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) {
@ -105,10 +105,11 @@ func (l *linkTLS) listen(url *url.URL, sintf string) (*Listener, error) {
cancel() cancel()
break break
} }
addr := conn.RemoteAddr().(*net.TCPAddr) laddr := conn.LocalAddr().(*net.TCPAddr)
name := fmt.Sprintf("tls://%s", addr) raddr := conn.RemoteAddr().(*net.TCPAddr)
info := linkInfoFor("tls", sintf, strings.SplitN(addr.IP.String(), "%", 2)[0]) name := fmt.Sprintf("tls://%s", raddr)
if err = l.handler(name, info, conn, linkOptions{}, true); err != nil { info := linkInfoFor("tls", sintf, tcpIDFor(laddr, raddr))
if err = l.handler(name, info, conn, linkOptionsForListener(url), true, raddr.IP.IsLinkLocalUnicast()); err != nil {
l.core.log.Errorln("Failed to create inbound link:", err) l.core.log.Errorln("Failed to create inbound link:", err)
} }
} }
@ -166,6 +167,6 @@ func (l *linkTLS) generateConfig() (*tls.Config, error) {
}, nil }, nil
} }
func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming bool) error { func (l *linkTLS) handler(name string, info linkInfo, conn net.Conn, options linkOptions, incoming, force bool) error {
return l.tcp.handler(name, info, conn, options, incoming) return l.tcp.handler(name, info, conn, options, incoming, force)
} }

View File

@ -2,7 +2,6 @@ package core
import ( import (
"context" "context"
"fmt"
"net" "net"
"net/url" "net/url"
"time" "time"
@ -36,7 +35,7 @@ func (l *links) newLinkUNIX() *linkUNIX {
func (l *linkUNIX) dial(url *url.URL, options linkOptions, _ string) error { func (l *linkUNIX) dial(url *url.URL, options linkOptions, _ string) error {
info := linkInfoFor("unix", "", url.Path) info := linkInfoFor("unix", "", url.Path)
if l.links.isConnectedTo(info) { if l.links.isConnectedTo(info) {
return fmt.Errorf("duplicate connection attempt") return nil
} }
addr, err := net.ResolveUnixAddr("unix", url.Path) addr, err := net.ResolveUnixAddr("unix", url.Path)
if err != nil { if err != nil {
@ -75,7 +74,7 @@ func (l *linkUNIX) listen(url *url.URL, _ string) (*Listener, error) {
break break
} }
info := linkInfoFor("unix", "", url.String()) info := linkInfoFor("unix", "", url.String())
if err = l.handler(url.String(), info, conn, linkOptions{}, true); err != nil { if err = l.handler(url.String(), info, conn, linkOptionsForListener(url), true); err != nil {
l.core.log.Errorln("Failed to create inbound link:", err) l.core.log.Errorln("Failed to create inbound link:", err)
} }
} }

View File

@ -37,11 +37,12 @@ type Multicast struct {
} }
type interfaceInfo struct { type interfaceInfo struct {
iface net.Interface iface net.Interface
addrs []net.Addr addrs []net.Addr
beacon bool beacon bool
listen bool listen bool
port uint16 port uint16
priority uint8
} }
type listenerInfo struct { type listenerInfo struct {
@ -77,7 +78,11 @@ func (m *Multicast) _start() error {
if m._isOpen { if m._isOpen {
return fmt.Errorf("multicast module is already started") return fmt.Errorf("multicast module is already started")
} }
if len(m.config._interfaces) == 0 { var anyEnabled bool
for intf := range m.config._interfaces {
anyEnabled = anyEnabled || intf.Beacon || intf.Listen
}
if !anyEnabled {
return nil return nil
} }
m.log.Debugln("Starting multicast module") m.log.Debugln("Starting multicast module")
@ -190,10 +195,11 @@ func (m *Multicast) _getAllowedInterfaces() map[string]*interfaceInfo {
continue continue
} }
interfaces[iface.Name] = &interfaceInfo{ interfaces[iface.Name] = &interfaceInfo{
iface: iface, iface: iface,
beacon: ifcfg.Beacon, beacon: ifcfg.Beacon,
listen: ifcfg.Listen, listen: ifcfg.Listen,
port: ifcfg.Port, port: ifcfg.Port,
priority: ifcfg.Priority,
} }
break break
} }
@ -383,7 +389,7 @@ func (m *Multicast) listen() {
}) })
if info, ok := interfaces[from.Zone]; ok && info.listen { if info, ok := interfaces[from.Zone]; ok && info.listen {
addr.Zone = "" addr.Zone = ""
pin := fmt.Sprintf("/?key=%s", hex.EncodeToString(key)) pin := fmt.Sprintf("/?key=%s&priority=%d", hex.EncodeToString(key), info.priority)
u, err := url.Parse("tls://" + addr.String() + pin) u, err := url.Parse("tls://" + addr.String() + pin)
if err != nil { if err != nil {
m.log.Debugln("Call from multicast failed, parse error:", addr.String(), err) m.log.Debugln("Call from multicast failed, parse error:", addr.String(), err)

View File

@ -15,15 +15,19 @@ func (m *Multicast) _multicastStarted() {
func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error { func (m *Multicast) multicastReuse(network string, address string, c syscall.RawConn) error {
var control error var control error
var reuseport error var reuseaddr error
control = c.Control(func(fd uintptr) { control = c.Control(func(fd uintptr) {
reuseport = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) // Previously we used SO_REUSEPORT here, but that meant that machines running
// Yggdrasil nodes as different users would inevitably fail with EADDRINUSE.
// The behaviour for multicast is similar with both, so we'll use SO_REUSEADDR
// instead.
reuseaddr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
}) })
switch { switch {
case reuseport != nil: case reuseaddr != nil:
return reuseport return reuseaddr
default: default:
return control return control
} }

View File

@ -16,10 +16,11 @@ type SetupOption interface {
} }
type MulticastInterface struct { type MulticastInterface struct {
Regex *regexp.Regexp Regex *regexp.Regexp
Beacon bool Beacon bool
Listen bool Listen bool
Port uint16 Port uint16
Priority uint8
} }
type GroupAddress string type GroupAddress string