virtual-net: initial (#4751)

This commit is contained in:
fatedier
2025-04-16 16:05:54 +08:00
committed by GitHub
parent 773169e0c4
commit a78814a2e9
48 changed files with 1822 additions and 180 deletions

View File

@@ -14,13 +14,11 @@
//go:build !frps
package plugin
package client
import (
"context"
"io"
stdlog "log"
"net"
"net/http"
"net/http/httputil"
@@ -42,7 +40,7 @@ type HTTP2HTTPPlugin struct {
s *http.Server
}
func NewHTTP2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
func NewHTTP2HTTPPlugin(_ PluginContext, options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.HTTP2HTTPPluginOptions)
listener := NewProxyListener()
@@ -80,8 +78,8 @@ func NewHTTP2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
return p, nil
}
func (p *HTTP2HTTPPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
func (p *HTTP2HTTPPlugin) Handle(_ context.Context, connInfo *ConnectionInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(connInfo.Conn, connInfo.UnderlyingConn)
_ = p.l.PutConn(wrapConn)
}

View File

@@ -14,14 +14,12 @@
//go:build !frps
package plugin
package client
import (
"context"
"crypto/tls"
"io"
stdlog "log"
"net"
"net/http"
"net/http/httputil"
@@ -43,7 +41,7 @@ type HTTP2HTTPSPlugin struct {
s *http.Server
}
func NewHTTP2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
func NewHTTP2HTTPSPlugin(_ PluginContext, options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.HTTP2HTTPSPluginOptions)
listener := NewProxyListener()
@@ -89,8 +87,8 @@ func NewHTTP2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
return p, nil
}
func (p *HTTP2HTTPSPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
func (p *HTTP2HTTPSPlugin) Handle(_ context.Context, connInfo *ConnectionInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(connInfo.Conn, connInfo.UnderlyingConn)
_ = p.l.PutConn(wrapConn)
}

View File

@@ -14,7 +14,7 @@
//go:build !frps
package plugin
package client
import (
"bufio"
@@ -45,7 +45,7 @@ type HTTPProxy struct {
s *http.Server
}
func NewHTTPProxyPlugin(options v1.ClientPluginOptions) (Plugin, error) {
func NewHTTPProxyPlugin(_ PluginContext, options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.HTTPProxyPluginOptions)
listener := NewProxyListener()
@@ -69,8 +69,8 @@ func (hp *HTTPProxy) Name() string {
return v1.PluginHTTPProxy
}
func (hp *HTTPProxy) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
func (hp *HTTPProxy) Handle(_ context.Context, connInfo *ConnectionInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(connInfo.Conn, connInfo.UnderlyingConn)
sc, rd := libnet.NewSharedConn(wrapConn)
firstBytes := make([]byte, 7)

View File

@@ -14,15 +14,13 @@
//go:build !frps
package plugin
package client
import (
"context"
"crypto/tls"
"fmt"
"io"
stdlog "log"
"net"
"net/http"
"net/http/httputil"
"time"
@@ -48,7 +46,7 @@ type HTTPS2HTTPPlugin struct {
s *http.Server
}
func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
func NewHTTPS2HTTPPlugin(_ PluginContext, options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.HTTPS2HTTPPluginOptions)
listener := NewProxyListener()
@@ -106,10 +104,10 @@ func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
return p, nil
}
func (p *HTTPS2HTTPPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
if extra.SrcAddr != nil {
wrapConn.SetRemoteAddr(extra.SrcAddr)
func (p *HTTPS2HTTPPlugin) Handle(_ context.Context, connInfo *ConnectionInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(connInfo.Conn, connInfo.UnderlyingConn)
if connInfo.SrcAddr != nil {
wrapConn.SetRemoteAddr(connInfo.SrcAddr)
}
_ = p.l.PutConn(wrapConn)
}

View File

@@ -14,15 +14,13 @@
//go:build !frps
package plugin
package client
import (
"context"
"crypto/tls"
"fmt"
"io"
stdlog "log"
"net"
"net/http"
"net/http/httputil"
"time"
@@ -48,7 +46,7 @@ type HTTPS2HTTPSPlugin struct {
s *http.Server
}
func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
func NewHTTPS2HTTPSPlugin(_ PluginContext, options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.HTTPS2HTTPSPluginOptions)
listener := NewProxyListener()
@@ -112,10 +110,10 @@ func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
return p, nil
}
func (p *HTTPS2HTTPSPlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
if extra.SrcAddr != nil {
wrapConn.SetRemoteAddr(extra.SrcAddr)
func (p *HTTPS2HTTPSPlugin) Handle(_ context.Context, connInfo *ConnectionInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(connInfo.Conn, connInfo.UnderlyingConn)
if connInfo.SrcAddr != nil {
wrapConn.SetRemoteAddr(connInfo.SrcAddr)
}
_ = p.l.PutConn(wrapConn)
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package plugin
package client
import (
"context"
@@ -25,13 +25,18 @@ import (
pp "github.com/pires/go-proxyproto"
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/vnet"
)
type PluginContext struct {
Name string
VnetController *vnet.Controller
}
// Creators is used for create plugins to handle connections.
var creators = make(map[string]CreatorFn)
// params has prefix "plugin_"
type CreatorFn func(options v1.ClientPluginOptions) (Plugin, error)
type CreatorFn func(pluginCtx PluginContext, options v1.ClientPluginOptions) (Plugin, error)
func Register(name string, fn CreatorFn) {
if _, exist := creators[name]; exist {
@@ -40,16 +45,19 @@ func Register(name string, fn CreatorFn) {
creators[name] = fn
}
func Create(name string, options v1.ClientPluginOptions) (p Plugin, err error) {
if fn, ok := creators[name]; ok {
p, err = fn(options)
func Create(pluginName string, pluginCtx PluginContext, options v1.ClientPluginOptions) (p Plugin, err error) {
if fn, ok := creators[pluginName]; ok {
p, err = fn(pluginCtx, options)
} else {
err = fmt.Errorf("plugin [%s] is not registered", name)
err = fmt.Errorf("plugin [%s] is not registered", pluginName)
}
return
}
type ExtraInfo struct {
type ConnectionInfo struct {
Conn io.ReadWriteCloser
UnderlyingConn net.Conn
ProxyProtocolHeader *pp.Header
SrcAddr net.Addr
DstAddr net.Addr
@@ -58,7 +66,7 @@ type ExtraInfo struct {
type Plugin interface {
Name() string
Handle(ctx context.Context, conn io.ReadWriteCloser, realConn net.Conn, extra *ExtraInfo)
Handle(ctx context.Context, connInfo *ConnectionInfo)
Close() error
}

View File

@@ -14,13 +14,12 @@
//go:build !frps
package plugin
package client
import (
"context"
"io"
"log"
"net"
gosocks5 "github.com/armon/go-socks5"
@@ -36,7 +35,7 @@ type Socks5Plugin struct {
Server *gosocks5.Server
}
func NewSocks5Plugin(options v1.ClientPluginOptions) (p Plugin, err error) {
func NewSocks5Plugin(_ PluginContext, options v1.ClientPluginOptions) (p Plugin, err error) {
opts := options.(*v1.Socks5PluginOptions)
cfg := &gosocks5.Config{
@@ -51,9 +50,9 @@ func NewSocks5Plugin(options v1.ClientPluginOptions) (p Plugin, err error) {
return
}
func (sp *Socks5Plugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
defer conn.Close()
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
func (sp *Socks5Plugin) Handle(_ context.Context, connInfo *ConnectionInfo) {
defer connInfo.Conn.Close()
wrapConn := netpkg.WrapReadWriteCloserToConn(connInfo.Conn, connInfo.UnderlyingConn)
_ = sp.Server.ServeConn(wrapConn)
}

View File

@@ -14,12 +14,10 @@
//go:build !frps
package plugin
package client
import (
"context"
"io"
"net"
"net/http"
"time"
@@ -40,7 +38,7 @@ type StaticFilePlugin struct {
s *http.Server
}
func NewStaticFilePlugin(options v1.ClientPluginOptions) (Plugin, error) {
func NewStaticFilePlugin(_ PluginContext, options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.StaticFilePluginOptions)
listener := NewProxyListener()
@@ -70,8 +68,8 @@ func NewStaticFilePlugin(options v1.ClientPluginOptions) (Plugin, error) {
return sp, nil
}
func (sp *StaticFilePlugin) Handle(_ context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
func (sp *StaticFilePlugin) Handle(_ context.Context, connInfo *ConnectionInfo) {
wrapConn := netpkg.WrapReadWriteCloserToConn(connInfo.Conn, connInfo.UnderlyingConn)
_ = sp.l.PutConn(wrapConn)
}

View File

@@ -14,12 +14,11 @@
//go:build !frps
package plugin
package client
import (
"context"
"crypto/tls"
"io"
"net"
libio "github.com/fatedier/golib/io"
@@ -40,7 +39,7 @@ type TLS2RawPlugin struct {
tlsConfig *tls.Config
}
func NewTLS2RawPlugin(options v1.ClientPluginOptions) (Plugin, error) {
func NewTLS2RawPlugin(_ PluginContext, options v1.ClientPluginOptions) (Plugin, error) {
opts := options.(*v1.TLS2RawPluginOptions)
p := &TLS2RawPlugin{
@@ -55,10 +54,10 @@ func NewTLS2RawPlugin(options v1.ClientPluginOptions) (Plugin, error) {
return p, nil
}
func (p *TLS2RawPlugin) Handle(ctx context.Context, conn io.ReadWriteCloser, realConn net.Conn, _ *ExtraInfo) {
func (p *TLS2RawPlugin) Handle(ctx context.Context, connInfo *ConnectionInfo) {
xl := xlog.FromContextSafe(ctx)
wrapConn := netpkg.WrapReadWriteCloserToConn(conn, realConn)
wrapConn := netpkg.WrapReadWriteCloserToConn(connInfo.Conn, connInfo.UnderlyingConn)
tlsConn := tls.Server(wrapConn, p.tlsConfig)
if err := tlsConn.Handshake(); err != nil {

View File

@@ -14,11 +14,10 @@
//go:build !frps
package plugin
package client
import (
"context"
"io"
"net"
libio "github.com/fatedier/golib/io"
@@ -35,7 +34,7 @@ type UnixDomainSocketPlugin struct {
UnixAddr *net.UnixAddr
}
func NewUnixDomainSocketPlugin(options v1.ClientPluginOptions) (p Plugin, err error) {
func NewUnixDomainSocketPlugin(_ PluginContext, options v1.ClientPluginOptions) (p Plugin, err error) {
opts := options.(*v1.UnixDomainSocketPluginOptions)
unixAddr, errRet := net.ResolveUnixAddr("unix", opts.UnixPath)
@@ -50,20 +49,20 @@ func NewUnixDomainSocketPlugin(options v1.ClientPluginOptions) (p Plugin, err er
return
}
func (uds *UnixDomainSocketPlugin) Handle(ctx context.Context, conn io.ReadWriteCloser, _ net.Conn, extra *ExtraInfo) {
func (uds *UnixDomainSocketPlugin) Handle(ctx context.Context, connInfo *ConnectionInfo) {
xl := xlog.FromContextSafe(ctx)
localConn, err := net.DialUnix("unix", nil, uds.UnixAddr)
if err != nil {
xl.Warnf("dial to uds %s error: %v", uds.UnixAddr, err)
return
}
if extra.ProxyProtocolHeader != nil {
if _, err := extra.ProxyProtocolHeader.WriteTo(localConn); err != nil {
if connInfo.ProxyProtocolHeader != nil {
if _, err := connInfo.ProxyProtocolHeader.WriteTo(localConn); err != nil {
return
}
}
libio.Join(localConn, conn)
libio.Join(localConn, connInfo.Conn)
}
func (uds *UnixDomainSocketPlugin) Name() string {

View File

@@ -0,0 +1,71 @@
// 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"
v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/util/xlog"
)
func init() {
Register(v1.PluginVirtualNet, NewVirtualNetPlugin)
}
type VirtualNetPlugin struct {
pluginCtx PluginContext
opts *v1.VirtualNetPluginOptions
}
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) {
xl := xlog.FromContextSafe(ctx)
// Verify if virtual network controller is available
if p.pluginCtx.VnetController == nil {
return
}
// Register the connection with the controller
routeName := p.pluginCtx.Name
err := p.pluginCtx.VnetController.RegisterServerConn(ctx, routeName, connInfo.Conn)
if err != nil {
xl.Errorf("virtual net failed to register server connection: %v", err)
return
}
}
func (p *VirtualNetPlugin) Name() string {
return v1.PluginVirtualNet
}
func (p *VirtualNetPlugin) Close() error {
if p.pluginCtx.VnetController != nil {
p.pluginCtx.VnetController.UnregisterServerConn(p.pluginCtx.Name)
}
return nil
}