Co-authored-by: jdl <jdl@desktop> Co-authored-by: jdl <jdl@crumpington.com> Reviewed-on: #3
139 lines
3.4 KiB
Go
139 lines
3.4 KiB
Go
package peer
|
|
|
|
import (
|
|
"net/netip"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// TODO: Remove
|
|
func newRemotePeer(ip byte) *remotePeer {
|
|
counter := uint64(time.Now().Unix()<<30 + 1)
|
|
return &remotePeer{
|
|
IP: ip,
|
|
counter: &counter,
|
|
dupCheck: newDupCheck(0),
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type remotePeer struct {
|
|
localIP byte
|
|
IP byte // VPN IP of peer (last byte).
|
|
Up bool // True if data can be sent on the peer.
|
|
Relay bool // True if the peer is a relay.
|
|
Direct bool // True if this is a direct connection.
|
|
DirectAddr netip.AddrPort // Remote address if directly connected.
|
|
PubSignKey []byte
|
|
ControlCipher *controlCipher
|
|
DataCipher *dataCipher
|
|
|
|
counter *uint64 // For sending to. Atomic access only.
|
|
dupCheck *dupCheck // For receiving from. Not safe for concurrent use.
|
|
}
|
|
|
|
func (p remotePeer) EncryptDataPacket(destIP byte, data, out []byte) []byte {
|
|
h := header{
|
|
StreamID: dataStreamID,
|
|
Counter: atomic.AddUint64(p.counter, 1),
|
|
SourceIP: p.localIP,
|
|
DestIP: destIP,
|
|
}
|
|
return p.DataCipher.Encrypt(h, data, out)
|
|
}
|
|
|
|
// Decrypts and de-dups incoming data packets.
|
|
func (p remotePeer) DecryptDataPacket(h header, enc, out []byte) ([]byte, error) {
|
|
dec, ok := p.DataCipher.Decrypt(enc, out)
|
|
if !ok {
|
|
return nil, errDecryptionFailed
|
|
}
|
|
|
|
if p.dupCheck.IsDup(h.Counter) {
|
|
return nil, errDuplicateSeqNum
|
|
}
|
|
|
|
return dec, nil
|
|
}
|
|
|
|
// Peer must have a ControlCipher.
|
|
func (p remotePeer) EncryptControlPacket(pkt marshaller, tmp, out []byte) []byte {
|
|
tmp = pkt.Marshal(tmp)
|
|
h := header{
|
|
StreamID: controlStreamID,
|
|
Counter: atomic.AddUint64(p.counter, 1),
|
|
SourceIP: p.localIP,
|
|
DestIP: p.IP,
|
|
}
|
|
|
|
return p.ControlCipher.Encrypt(h, tmp, out)
|
|
}
|
|
|
|
// Returns a controlMsg[PacketType]. Peer must have a non-nil ControlCipher.
|
|
//
|
|
// This function also drops packets with duplicate sequence numbers.
|
|
func (p remotePeer) DecryptControlPacket(fromAddr netip.AddrPort, h header, enc, tmp []byte) (any, error) {
|
|
out, ok := p.ControlCipher.Decrypt(enc, tmp)
|
|
if !ok {
|
|
return nil, errDecryptionFailed
|
|
}
|
|
|
|
if p.dupCheck.IsDup(h.Counter) {
|
|
return nil, errDuplicateSeqNum
|
|
}
|
|
|
|
msg, err := parseControlMsg(h.SourceIP, fromAddr, out)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return msg, nil
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type routingTable struct {
|
|
// The LocalIP is the configured IP address of the local peer on the VPN.
|
|
//
|
|
// This value is constant.
|
|
LocalIP byte
|
|
|
|
// The LocalAddr is the configured local public address of the peer on the
|
|
// internet. If LocalAddr.IsValid(), then the local peer has a public
|
|
// address.
|
|
//
|
|
// This value is constant.
|
|
LocalAddr netip.AddrPort
|
|
|
|
// The remote peer configurations. These are updated by
|
|
Peers [256]remotePeer
|
|
|
|
// The current relay's VPN IP address, or zero if no relay is available.
|
|
RelayIP byte
|
|
}
|
|
|
|
func newRoutingTable(localIP byte, localAddr netip.AddrPort) routingTable {
|
|
rt := routingTable{
|
|
LocalIP: localIP,
|
|
LocalAddr: localAddr,
|
|
}
|
|
|
|
for i := range rt.Peers {
|
|
counter := uint64(time.Now().Unix()<<30 + 1)
|
|
rt.Peers[i] = remotePeer{
|
|
localIP: localIP,
|
|
IP: byte(i),
|
|
counter: &counter,
|
|
dupCheck: newDupCheck(0),
|
|
}
|
|
}
|
|
|
|
return rt
|
|
}
|
|
|
|
func (rt *routingTable) GetRelay() (remotePeer, bool) {
|
|
relay := rt.Peers[rt.RelayIP]
|
|
return relay, relay.Up && relay.Direct
|
|
}
|