package vpnruntime import ( "context" "errors" "sync/atomic" "time" "github.com/example/remote-access-platform/agents/rap-node-agent/internal/fabricproto" "github.com/example/remote-access-platform/agents/rap-node-agent/internal/mesh" ) type FabricSessionFrameSender interface { Send(context.Context, fabricproto.Frame) error } type FabricSessionFrameReceiver interface { Frames() <-chan fabricproto.Frame Errors() <-chan error } type FabricSessionPacketTransport struct { Sender FabricSessionFrameSender Receiver FabricSessionFrameReceiver Inbox *FabricPacketInbox StreamID uint64 VPNConnectionID string SendDirection string ReceiveDirection string TrafficClass string sequence uint64 } func (t *FabricSessionPacketTransport) SendGatewayPacketBatch(ctx context.Context, packets [][]byte) error { packets = cleanPacketBatch(packets) if len(packets) == 0 { return nil } if t == nil || t.Sender == nil { return mesh.ErrForwardRuntimeUnavailable } if t.StreamID == 0 || t.VPNConnectionID == "" { return errors.New("fabric session packet transport identity is incomplete") } direction := t.SendDirection if direction == "" { direction = FabricDirectionGatewayToClient } frame, err := NewFabricVPNPacketDataFrame(FabricVPNPacketFrameInput{ StreamID: t.StreamID, Sequence: atomic.AddUint64(&t.sequence, 1), VPNConnectionID: t.VPNConnectionID, Direction: direction, TrafficClass: t.TrafficClass, Packets: packets, }) if err != nil { return err } return t.Sender.Send(ctx, frame) } func (t *FabricSessionPacketTransport) ReceiveGatewayPacketBatch(ctx context.Context, timeout time.Duration) ([][]byte, error) { if t == nil || t.Inbox == nil { return nil, mesh.ErrForwardRuntimeUnavailable } direction := t.ReceiveDirection if direction == "" { direction = FabricDirectionClientToGateway } return t.Inbox.Receive(ctx, t.VPNConnectionID, direction, timeout) } func (t *FabricSessionPacketTransport) RunFrameIngress(ctx context.Context) error { if t == nil || t.Receiver == nil || t.Inbox == nil { return mesh.ErrForwardRuntimeUnavailable } frames := t.Receiver.Frames() errorsCh := t.Receiver.Errors() for { select { case <-ctx.Done(): return ctx.Err() case err, ok := <-errorsCh: if !ok { errorsCh = nil continue } if err != nil { return err } case frame, ok := <-frames: if !ok { return nil } if frame.Type != fabricproto.FrameData { continue } if t.StreamID != 0 && frame.StreamID != t.StreamID { continue } if err := t.Inbox.DeliverFabricSessionFrame(ctx, frame); err != nil { return err } } } }