frp/pkg/plugin/client/virtual_net.go
2025-04-27 15:22:28 +08:00

93 lines
2.3 KiB
Go

// Copyright 2025 The frp Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !frps
package client
import (
"context"
"io"
"sync"
v1 "github.com/fatedier/frp/pkg/config/v1"
)
func init() {
Register(v1.PluginVirtualNet, NewVirtualNetPlugin)
}
type VirtualNetPlugin struct {
pluginCtx PluginContext
opts *v1.VirtualNetPluginOptions
mu sync.Mutex
conns map[io.ReadWriteCloser]struct{}
}
func NewVirtualNetPlugin(pluginCtx PluginContext, options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.VirtualNetPluginOptions)
p := &VirtualNetPlugin{
pluginCtx: pluginCtx,
opts: opts,
}
return p, nil
}
func (p *VirtualNetPlugin) Handle(ctx context.Context, connInfo *ConnectionInfo) {
// Verify if virtual network controller is available
if p.pluginCtx.VnetController == nil {
return
}
// Add the connection before starting the read loop to avoid race condition
// where RemoveConn might be called before the connection is added.
p.mu.Lock()
if p.conns == nil {
p.conns = make(map[io.ReadWriteCloser]struct{})
}
p.conns[connInfo.Conn] = struct{}{}
p.mu.Unlock()
// Register the connection with the controller and pass the cleanup function
p.pluginCtx.VnetController.StartServerConnReadLoop(ctx, connInfo.Conn, func() {
p.RemoveConn(connInfo.Conn)
})
}
func (p *VirtualNetPlugin) RemoveConn(conn io.ReadWriteCloser) {
p.mu.Lock()
defer p.mu.Unlock()
// Check if the map exists, as Close might have set it to nil concurrently
if p.conns != nil {
delete(p.conns, conn)
}
}
func (p *VirtualNetPlugin) Name() string {
return v1.PluginVirtualNet
}
func (p *VirtualNetPlugin) Close() error {
p.mu.Lock()
defer p.mu.Unlock()
// Close any remaining connections
for conn := range p.conns {
_ = conn.Close()
}
p.conns = nil
return nil
}