рабочий вариант, но скороть 10 МБит
This commit is contained in:
@@ -3,6 +3,7 @@ package vpnruntime
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -12,10 +13,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
fabricVPNPacketPayloadMagic uint32 = 0x52565042 // RVPB
|
||||
fabricVPNPacketPayloadVersion uint8 = 1
|
||||
fabricVPNPacketPayloadHeader = 24
|
||||
fabricVPNPacketMaxPacketCount = 2048
|
||||
fabricVPNPacketPayloadMagic uint32 = 0x52565042 // RVPB
|
||||
fabricVPNPacketPayloadVersion uint8 = 2
|
||||
fabricVPNPacketPayloadHeader = 24
|
||||
fabricVPNPacketMaxPacketCount = 2048
|
||||
fabricVPNPacketMaxMetadataBytes = 64 * 1024
|
||||
|
||||
fabricVPNPacketDirectionClientToGateway uint8 = 1
|
||||
fabricVPNPacketDirectionGatewayToClient uint8 = 2
|
||||
@@ -32,6 +34,7 @@ type FabricVPNPacketFrameInput struct {
|
||||
VPNConnectionID string
|
||||
Direction string
|
||||
TrafficClass string
|
||||
ServiceTunnel FabricServiceTunnel
|
||||
Packets [][]byte
|
||||
Now time.Time
|
||||
}
|
||||
@@ -60,6 +63,26 @@ func NewFabricVPNPacketDataFrame(input FabricVPNPacketFrameInput) (fabricproto.F
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewFabricVPNSessionHelloFrame(input FabricVPNPacketFrameInput) (fabricproto.Frame, error) {
|
||||
if input.StreamID == 0 {
|
||||
return fabricproto.Frame{}, fmt.Errorf("%w: missing stream id", ErrFabricVPNPacketFrameInvalid)
|
||||
}
|
||||
if input.VPNConnectionID == "" || input.Direction == "" {
|
||||
return fabricproto.Frame{}, fmt.Errorf("%w: missing vpn identity", ErrFabricVPNPacketFrameInvalid)
|
||||
}
|
||||
payload, err := encodeFabricVPNPacketPayload(input, nil)
|
||||
if err != nil {
|
||||
return fabricproto.Frame{}, err
|
||||
}
|
||||
return fabricproto.Frame{
|
||||
Type: fabricproto.FrameData,
|
||||
TrafficClass: fabricFrameTrafficClass(input.TrafficClass, nil),
|
||||
StreamID: input.StreamID,
|
||||
Sequence: input.Sequence,
|
||||
Payload: payload,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DecodeFabricVPNPacketDataFrame(frame fabricproto.Frame) (mesh.VPNPacketBatchPayload, error) {
|
||||
if frame.Type != fabricproto.FrameData || frame.StreamID == 0 {
|
||||
return mesh.VPNPacketBatchPayload{}, fmt.Errorf("%w: expected DATA stream frame", ErrFabricVPNPacketFrameInvalid)
|
||||
@@ -94,11 +117,19 @@ func encodeFabricVPNPacketPayload(input FabricVPNPacketFrameInput, packets [][]b
|
||||
if len(vpnID) > 0xffff {
|
||||
return nil, fmt.Errorf("%w: vpn connection id too long", ErrFabricVPNPacketPayload)
|
||||
}
|
||||
var metadata []byte
|
||||
if len(packets) == 0 {
|
||||
var err error
|
||||
metadata, err = encodeFabricVPNPacketServiceMetadata(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
now := input.Now.UTC()
|
||||
if now.IsZero() {
|
||||
now = time.Now().UTC()
|
||||
}
|
||||
total := fabricVPNPacketPayloadHeader + len(vpnID)
|
||||
total := fabricVPNPacketPayloadHeader + len(vpnID) + len(metadata)
|
||||
for _, packet := range packets {
|
||||
total += 4 + len(packet)
|
||||
}
|
||||
@@ -108,10 +139,13 @@ func encodeFabricVPNPacketPayload(input FabricVPNPacketFrameInput, packets [][]b
|
||||
out[5] = directionCode
|
||||
binary.BigEndian.PutUint16(out[6:8], uint16(len(packets)))
|
||||
binary.BigEndian.PutUint16(out[8:10], uint16(len(vpnID)))
|
||||
binary.BigEndian.PutUint16(out[10:12], uint16(len(metadata)))
|
||||
binary.BigEndian.PutUint64(out[12:20], uint64(now.UnixNano()))
|
||||
offset := fabricVPNPacketPayloadHeader
|
||||
copy(out[offset:], vpnID)
|
||||
offset += len(vpnID)
|
||||
copy(out[offset:], metadata)
|
||||
offset += len(metadata)
|
||||
for _, packet := range packets {
|
||||
binary.BigEndian.PutUint32(out[offset:offset+4], uint32(len(packet)))
|
||||
offset += 4
|
||||
@@ -128,7 +162,8 @@ func decodeFabricVPNPacketPayload(payload []byte) (mesh.VPNPacketBatchPayload, e
|
||||
if binary.BigEndian.Uint32(payload[0:4]) != fabricVPNPacketPayloadMagic {
|
||||
return mesh.VPNPacketBatchPayload{}, fmt.Errorf("%w: bad magic", ErrFabricVPNPacketPayload)
|
||||
}
|
||||
if payload[4] != fabricVPNPacketPayloadVersion {
|
||||
version := payload[4]
|
||||
if version != 1 && version != fabricVPNPacketPayloadVersion {
|
||||
return mesh.VPNPacketBatchPayload{}, fmt.Errorf("%w: unsupported version %d", ErrFabricVPNPacketPayload, payload[4])
|
||||
}
|
||||
direction, err := fabricVPNPacketDirectionName(payload[5])
|
||||
@@ -137,7 +172,11 @@ func decodeFabricVPNPacketPayload(payload []byte) (mesh.VPNPacketBatchPayload, e
|
||||
}
|
||||
packetCount := int(binary.BigEndian.Uint16(payload[6:8]))
|
||||
vpnIDLength := int(binary.BigEndian.Uint16(payload[8:10]))
|
||||
if packetCount <= 0 || packetCount > fabricVPNPacketMaxPacketCount {
|
||||
metadataLength := 0
|
||||
if version >= 2 {
|
||||
metadataLength = int(binary.BigEndian.Uint16(payload[10:12]))
|
||||
}
|
||||
if packetCount < 0 || packetCount > fabricVPNPacketMaxPacketCount {
|
||||
return mesh.VPNPacketBatchPayload{}, fmt.Errorf("%w: invalid packet count %d", ErrFabricVPNPacketPayload, packetCount)
|
||||
}
|
||||
offset := fabricVPNPacketPayloadHeader
|
||||
@@ -149,6 +188,16 @@ func decodeFabricVPNPacketPayload(payload []byte) (mesh.VPNPacketBatchPayload, e
|
||||
if vpnID == "" {
|
||||
return mesh.VPNPacketBatchPayload{}, fmt.Errorf("%w: empty vpn id", ErrFabricVPNPacketPayload)
|
||||
}
|
||||
metadata := fabricVPNPacketServiceMetadata{}
|
||||
if metadataLength > 0 {
|
||||
if metadataLength > fabricVPNPacketMaxMetadataBytes || len(payload) < offset+metadataLength {
|
||||
return mesh.VPNPacketBatchPayload{}, fmt.Errorf("%w: truncated service metadata", ErrFabricVPNPacketPayload)
|
||||
}
|
||||
if err := json.Unmarshal(payload[offset:offset+metadataLength], &metadata); err != nil {
|
||||
return mesh.VPNPacketBatchPayload{}, fmt.Errorf("%w: invalid service metadata: %v", ErrFabricVPNPacketPayload, err)
|
||||
}
|
||||
offset += metadataLength
|
||||
}
|
||||
packets := make([][]byte, 0, packetCount)
|
||||
for index := 0; index < packetCount; index++ {
|
||||
if len(payload) < offset+4 {
|
||||
@@ -169,12 +218,74 @@ func decodeFabricVPNPacketPayload(payload []byte) (mesh.VPNPacketBatchPayload, e
|
||||
return mesh.VPNPacketBatchPayload{
|
||||
SchemaVersion: "rap.vpn_packet_batch.fabric.v1",
|
||||
VPNConnectionID: vpnID,
|
||||
TunnelID: firstNonEmptyTunnelString(metadata.TunnelID, vpnID),
|
||||
PoolID: metadata.PoolID,
|
||||
ServiceID: metadata.ServiceID,
|
||||
LocalServiceID: metadata.LocalServiceID,
|
||||
RemoteServiceID: metadata.RemoteServiceID,
|
||||
ServiceKind: metadata.ServiceKind,
|
||||
ServiceClass: metadata.ServiceClass,
|
||||
ServiceRole: metadata.ServiceRole,
|
||||
RouteLeaseID: metadata.RouteLeaseID,
|
||||
RouteGeneration: metadata.RouteGeneration,
|
||||
DataPlane: metadata.DataPlane,
|
||||
TransportOwner: metadata.TransportOwner,
|
||||
RouteVisibility: metadata.RouteVisibility,
|
||||
TrafficClasses: metadata.TrafficClasses,
|
||||
StreamShards: metadata.StreamShards,
|
||||
Direction: direction,
|
||||
Packets: packets,
|
||||
SentAt: sentAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type fabricVPNPacketServiceMetadata struct {
|
||||
TunnelID string `json:"tunnel_id,omitempty"`
|
||||
PoolID string `json:"pool_id,omitempty"`
|
||||
ServiceID string `json:"service_id,omitempty"`
|
||||
LocalServiceID string `json:"local_service_id,omitempty"`
|
||||
RemoteServiceID string `json:"remote_service_id,omitempty"`
|
||||
ServiceKind string `json:"service_kind,omitempty"`
|
||||
ServiceClass string `json:"service_class,omitempty"`
|
||||
ServiceRole string `json:"service_role,omitempty"`
|
||||
RouteLeaseID string `json:"route_lease_id,omitempty"`
|
||||
RouteGeneration string `json:"route_generation,omitempty"`
|
||||
DataPlane string `json:"data_plane,omitempty"`
|
||||
TransportOwner string `json:"transport_owner,omitempty"`
|
||||
RouteVisibility string `json:"route_visibility,omitempty"`
|
||||
TrafficClasses []string `json:"traffic_classes,omitempty"`
|
||||
StreamShards int `json:"stream_shards,omitempty"`
|
||||
}
|
||||
|
||||
func encodeFabricVPNPacketServiceMetadata(input FabricVPNPacketFrameInput) ([]byte, error) {
|
||||
tunnel := NormalizeServiceTunnel(input.ServiceTunnel, input.VPNConnectionID)
|
||||
metadata := fabricVPNPacketServiceMetadata{
|
||||
TunnelID: firstNonEmptyTunnelString(tunnel.TunnelID, input.VPNConnectionID),
|
||||
PoolID: tunnel.PoolID,
|
||||
ServiceID: tunnel.ServiceID,
|
||||
LocalServiceID: tunnel.LocalServiceID,
|
||||
RemoteServiceID: tunnel.RemoteServiceID,
|
||||
ServiceKind: tunnel.ServiceKind,
|
||||
ServiceClass: tunnel.ServiceClass,
|
||||
ServiceRole: tunnel.ServiceRole,
|
||||
RouteLeaseID: tunnel.RouteLeaseID,
|
||||
RouteGeneration: tunnel.RouteGeneration,
|
||||
DataPlane: tunnel.DataPlane,
|
||||
TransportOwner: tunnel.TransportOwner,
|
||||
RouteVisibility: tunnel.RouteVisibility,
|
||||
TrafficClasses: append([]string(nil), tunnel.TrafficClasses...),
|
||||
StreamShards: tunnel.StreamShards,
|
||||
}
|
||||
payload, err := json.Marshal(metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(payload) > fabricVPNPacketMaxMetadataBytes || len(payload) > 0xffff {
|
||||
return nil, fmt.Errorf("%w: service metadata too large", ErrFabricVPNPacketPayload)
|
||||
}
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
func fabricVPNPacketDirectionCode(direction string) (uint8, error) {
|
||||
switch direction {
|
||||
case FabricDirectionClientToGateway:
|
||||
@@ -201,6 +312,8 @@ func fabricFrameTrafficClass(trafficClass string, packets [][]byte) fabricproto.
|
||||
switch normalizeFabricTrafficClass(trafficClass) {
|
||||
case FabricTrafficClassControl:
|
||||
return fabricproto.TrafficClassControl
|
||||
case FabricTrafficClassDNS:
|
||||
return fabricproto.TrafficClassReliable
|
||||
case FabricTrafficClassInteractive:
|
||||
return fabricproto.TrafficClassInteractive
|
||||
case FabricTrafficClassReliable:
|
||||
@@ -208,9 +321,6 @@ func fabricFrameTrafficClass(trafficClass string, packets [][]byte) fabricproto.
|
||||
case FabricTrafficClassDroppable:
|
||||
return fabricproto.TrafficClassDroppable
|
||||
default:
|
||||
if batchHasTCPControlPacket(packets) {
|
||||
return fabricproto.TrafficClassInteractive
|
||||
}
|
||||
return fabricproto.TrafficClassBulk
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user