-
Notifications
You must be signed in to change notification settings - Fork 458
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: remove pkg/resources dependencies on wgtypes, netx
This finishes the work started in #4469, #4524. The only thing left for #4420 is to move the package in the source tree. Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
- Loading branch information
Showing
29 changed files
with
983 additions
and
809 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
package kubespan | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
|
||
"github.com/mdlayher/netx/eui64" | ||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" | ||
"inet.af/netaddr" | ||
|
||
"github.com/talos-systems/talos/pkg/resources/kubespan" | ||
"github.com/talos-systems/talos/pkg/resources/network" | ||
) | ||
|
||
// IdentitySpec adapter provides identity generation. | ||
// | ||
//nolint:revive,golint | ||
func IdentitySpec(r *kubespan.IdentitySpec) identity { | ||
return identity{ | ||
IdentitySpec: r, | ||
} | ||
} | ||
|
||
type identity struct { | ||
*kubespan.IdentitySpec | ||
} | ||
|
||
// GenerateKey generates new Wireguard key. | ||
func (a identity) GenerateKey() error { | ||
key, err := wgtypes.GeneratePrivateKey() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
a.IdentitySpec.PrivateKey = key.String() | ||
a.IdentitySpec.PublicKey = key.PublicKey().String() | ||
|
||
return nil | ||
} | ||
|
||
// UpdateAddress re-calculates node address based on input data. | ||
func (a identity) UpdateAddress(clusterID string, mac net.HardwareAddr) error { | ||
a.IdentitySpec.Subnet = network.ULAPrefix(clusterID, network.ULAKubeSpan) | ||
|
||
var err error | ||
|
||
a.IdentitySpec.Address, err = wgEUI64(a.IdentitySpec.Subnet, mac) | ||
|
||
return err | ||
} | ||
|
||
func wgEUI64(prefix netaddr.IPPrefix, mac net.HardwareAddr) (out netaddr.IPPrefix, err error) { | ||
if prefix.IsZero() { | ||
return out, fmt.Errorf("cannot calculate IP from zero prefix") | ||
} | ||
|
||
stdIP, err := eui64.ParseMAC(prefix.IPNet().IP, mac) | ||
if err != nil { | ||
return out, fmt.Errorf("failed to parse MAC into EUI-64 address: %w", err) | ||
} | ||
|
||
ip, ok := netaddr.FromStdIP(stdIP) | ||
if !ok { | ||
return out, fmt.Errorf("failed to parse intermediate standard IP %q: %w", stdIP.String(), err) | ||
} | ||
|
||
return netaddr.IPPrefixFrom(ip, ip.BitLen()), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
// Package kubespan implements adapters wrapping resources/kubespan to provide additional functionality. | ||
package kubespan |
158 changes: 158 additions & 0 deletions
158
internal/app/machined/pkg/adapters/kubespan/peer_status.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
// This Source Code Form is subject to the terms of the Mozilla Public | ||
// License, v. 2.0. If a copy of the MPL was not distributed with this | ||
// file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
package kubespan | ||
|
||
import ( | ||
"time" | ||
|
||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" | ||
"inet.af/netaddr" | ||
|
||
"github.com/talos-systems/talos/pkg/resources/kubespan" | ||
) | ||
|
||
// PeerStatusSpec adapter provides Wiregard integration and state management. | ||
// | ||
//nolint:revive,golint | ||
func PeerStatusSpec(r *kubespan.PeerStatusSpec) peerStatus { | ||
return peerStatus{ | ||
PeerStatusSpec: r, | ||
} | ||
} | ||
|
||
type peerStatus struct { | ||
*kubespan.PeerStatusSpec | ||
} | ||
|
||
// PeerDownInterval is the time since last handshake when established peer is considered to be down. | ||
// | ||
// WG whitepaper defines a downed peer as being: | ||
// Handshake Timeout (180s) + Rekey Timeout (5s) + Rekey Attempt Timeout (90s) | ||
// | ||
// This interval is applied when the link is already established. | ||
const PeerDownInterval = (180 + 5 + 90) * time.Second | ||
|
||
// EndpointConnectionTimeout is time to wait for initial handshake when the endpoint is just set. | ||
const EndpointConnectionTimeout = 15 * time.Second | ||
|
||
// CalculateState updates connection state based on other fields values. | ||
// | ||
// Goal: endpoint is ultimately down if we haven't seen handshake for more than peerDownInterval, | ||
// but as the endpoints get updated we want faster feedback, so we start checking more aggressively | ||
// that the handshake happened within endpointConnectionTimeout since last endpoint change. | ||
// | ||
// Timeline: | ||
// | ||
// ----------------------------------------------------------------------> | ||
// ^ ^ ^ | ||
// | | | | ||
// T0 T0+endpointConnectionTimeout T0+peerDownInterval | ||
// | ||
// Where T0 = LastEndpontChange | ||
// | ||
// The question is where is LastHandshakeTimeout vs. those points above: | ||
// | ||
// * if we're past (T0+peerDownInterval), simply check that time since last handshake < peerDownInterval | ||
// * if we're between (T0+endpointConnectionTimeout) and (T0+peerDownInterval), and there's no handshake | ||
// after the endpoint change, assume that the endpoint is down | ||
// * if we're between (T0) and (T0+endpointConnectionTimeout), and there's no handshake since the endpoint change, | ||
// consider the state to be unknown | ||
func (a peerStatus) CalculateState() { | ||
sinceLastHandshake := time.Since(a.PeerStatusSpec.LastHandshakeTime) | ||
sinceEndpointChange := time.Since(a.PeerStatusSpec.LastEndpointChange) | ||
|
||
a.CalculateStateWithDurations(sinceLastHandshake, sinceEndpointChange) | ||
} | ||
|
||
// CalculateStateWithDurations calculates the state based on the time since events. | ||
func (a peerStatus) CalculateStateWithDurations(sinceLastHandshake, sinceEndpointChange time.Duration) { | ||
switch { | ||
case sinceEndpointChange > PeerDownInterval: // past T0+peerDownInterval | ||
// if we got handshake in the last peerDownInterval, endpoint is up | ||
if sinceLastHandshake < PeerDownInterval { | ||
a.PeerStatusSpec.State = kubespan.PeerStateUp | ||
} else { | ||
a.PeerStatusSpec.State = kubespan.PeerStateDown | ||
} | ||
case sinceEndpointChange < EndpointConnectionTimeout: // between (T0) and (T0+endpointConnectionTimeout) | ||
// endpoint got recently updated, consider no handshake as 'unknown' | ||
if a.PeerStatusSpec.LastHandshakeTime.After(a.PeerStatusSpec.LastEndpointChange) { | ||
a.PeerStatusSpec.State = kubespan.PeerStateUp | ||
} else { | ||
a.PeerStatusSpec.State = kubespan.PeerStateUnknown | ||
} | ||
|
||
default: // otherwise, we're between (T0+endpointConnectionTimeout) and (T0+peerDownInterval) | ||
// if we haven't had the handshake yet, consider the endpoint to be down | ||
if a.PeerStatusSpec.LastHandshakeTime.After(a.PeerStatusSpec.LastEndpointChange) { | ||
a.PeerStatusSpec.State = kubespan.PeerStateUp | ||
} else { | ||
a.PeerStatusSpec.State = kubespan.PeerStateDown | ||
} | ||
} | ||
|
||
if a.PeerStatusSpec.State == kubespan.PeerStateDown && a.PeerStatusSpec.LastUsedEndpoint.IsZero() { | ||
// no endpoint, so unknown | ||
a.PeerStatusSpec.State = kubespan.PeerStateUnknown | ||
} | ||
} | ||
|
||
// UpdateFromWireguard updates fields from wgtypes information. | ||
func (a peerStatus) UpdateFromWireguard(peer wgtypes.Peer) { | ||
if peer.Endpoint != nil { | ||
a.PeerStatusSpec.Endpoint, _ = netaddr.FromStdAddr(peer.Endpoint.IP, peer.Endpoint.Port, "") | ||
} else { | ||
a.PeerStatusSpec.Endpoint = netaddr.IPPort{} | ||
} | ||
|
||
a.PeerStatusSpec.LastHandshakeTime = peer.LastHandshakeTime | ||
a.PeerStatusSpec.TransmitBytes = peer.TransmitBytes | ||
a.PeerStatusSpec.ReceiveBytes = peer.ReceiveBytes | ||
} | ||
|
||
// UpdateEndpoint updates the endpoint information and last update timestamp. | ||
func (a peerStatus) UpdateEndpoint(endpoint netaddr.IPPort) { | ||
a.PeerStatusSpec.Endpoint = endpoint | ||
a.PeerStatusSpec.LastUsedEndpoint = endpoint | ||
a.PeerStatusSpec.LastEndpointChange = time.Now() | ||
a.PeerStatusSpec.State = kubespan.PeerStateUnknown | ||
} | ||
|
||
// ShouldChangeEndpoint tells whether endpoint should be updated. | ||
func (a peerStatus) ShouldChangeEndpoint() bool { | ||
return a.PeerStatusSpec.State == kubespan.PeerStateDown || a.PeerStatusSpec.LastUsedEndpoint.IsZero() | ||
} | ||
|
||
// PickNewEndpoint picks new endpoint given the state and list of available endpoints. | ||
// | ||
// If returned newEndpoint is zero value, no new endpoint is available. | ||
func (a peerStatus) PickNewEndpoint(endpoints []netaddr.IPPort) (newEndpoint netaddr.IPPort) { | ||
if len(endpoints) == 0 { | ||
return | ||
} | ||
|
||
if a.PeerStatusSpec.LastUsedEndpoint.IsZero() { | ||
// first time setting the endpoint | ||
newEndpoint = endpoints[0] | ||
} else { | ||
// find the next endpoint after LastUsedEndpoint and use it | ||
idx := -1 | ||
|
||
for i := range endpoints { | ||
if endpoints[i] == a.PeerStatusSpec.LastUsedEndpoint { | ||
idx = i | ||
|
||
break | ||
} | ||
} | ||
|
||
// special case: if the peer has just a single endpoint, we can't rotate | ||
if !(len(endpoints) == 1 && idx == 0 && a.PeerStatusSpec.Endpoint == a.PeerStatusSpec.LastUsedEndpoint) { | ||
newEndpoint = endpoints[(idx+1)%len(endpoints)] | ||
} | ||
} | ||
|
||
return | ||
} |
Oops, something went wrong.