Skip to content

Commit c5a8f6e

Browse files
authored
Merge pull request #4753 from fatedier/dev
bump version
2 parents 31b44c1 + e208043 commit c5a8f6e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1823
-179
lines changed

README.md

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@ frp is an open source project with its ongoing development made possible entirel
1818
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_jetbrains.jpg">
1919
</a>
2020
</p>
21-
<p align="center">
22-
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
23-
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
24-
</a>
25-
</p>
2621
<p align="center">
2722
<a href="https://github.com/daytonaio/daytona" target="_blank">
2823
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
@@ -97,6 +92,11 @@ frp also offers a P2P connect mode.
9792
* [Client Plugins](#client-plugins)
9893
* [Server Manage Plugins](#server-manage-plugins)
9994
* [SSH Tunnel Gateway](#ssh-tunnel-gateway)
95+
* [Virtual Network (VirtualNet)](#virtual-network-virtualnet)
96+
* [Feature Gates](#feature-gates)
97+
* [Available Feature Gates](#available-feature-gates)
98+
* [Enabling Feature Gates](#enabling-feature-gates)
99+
* [Feature Lifecycle](#feature-lifecycle)
100100
* [Related Projects](#related-projects)
101101
* [Contributing](#contributing)
102102
* [Donation](#donation)
@@ -1260,6 +1260,41 @@ frpc tcp --proxy_name "test-tcp" --local_ip 127.0.0.1 --local_port 8080 --remote
12601260

12611261
Please refer to this [document](/doc/ssh_tunnel_gateway.md) for more information.
12621262

1263+
### Virtual Network (VirtualNet)
1264+
1265+
*Alpha feature added in v0.62.0*
1266+
1267+
The VirtualNet feature enables frp to create and manage virtual network connections between clients and visitors through a TUN interface. This allows for IP-level routing between machines, extending frp beyond simple port forwarding to support full network connectivity.
1268+
1269+
For detailed information about configuration and usage, please refer to the [VirtualNet documentation](/doc/virtual_net.md).
1270+
1271+
## Feature Gates
1272+
1273+
frp supports feature gates to enable or disable experimental features. This allows users to try out new features before they're considered stable.
1274+
1275+
### Available Feature Gates
1276+
1277+
| Name | Stage | Default | Description |
1278+
|------|-------|---------|-------------|
1279+
| VirtualNet | ALPHA | false | Virtual network capabilities for frp |
1280+
1281+
### Enabling Feature Gates
1282+
1283+
To enable an experimental feature, add the feature gate to your configuration:
1284+
1285+
```toml
1286+
featureGates = {
1287+
VirtualNet = true
1288+
}
1289+
```
1290+
1291+
### Feature Lifecycle
1292+
1293+
Features typically go through three stages:
1294+
1. **ALPHA**: Disabled by default, may be unstable
1295+
2. **BETA**: May be enabled by default, more stable but still evolving
1296+
3. **GA (Generally Available)**: Enabled by default, ready for production use
1297+
12631298
## Related Projects
12641299

12651300
* [gofrp/plugin](https://github.com/gofrp/plugin) - A repository for frp plugins that contains a variety of plugins implemented based on the frp extension mechanism, meeting the customization needs of different scenarios.

README_zh.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,6 @@ frp 是一个完全开源的项目,我们的开发工作完全依靠赞助者
2020
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_jetbrains.jpg">
2121
</a>
2222
</p>
23-
<p align="center">
24-
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
25-
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
26-
</a>
27-
</p>
2823
<p align="center">
2924
<a href="https://github.com/daytonaio/daytona" target="_blank">
3025
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">

Release.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
### Features
1+
### Notes
22

3-
* Support metadatas and annotations in frpc proxy commands.
3+
* **Feature Gates Introduced:** This version introduces a new experimental mechanism called Feature Gates. This allows users to enable or disable specific experimental features before they become generally available. Feature gates can be configured in the `featureGates` map within the configuration file.
4+
* **VirtualNet Feature Gate:** The first available feature gate is `VirtualNet`, which enables the experimental Virtual Network functionality (currently in Alpha stage).
45

5-
### Fixes
6+
### Features
67

7-
* Properly release resources in service.Close() to prevent resource leaks when used as a library.
8+
* **Virtual Network (VirtualNet):** Introduce experimental virtual network capabilities (Alpha). This allows creating a TUN device managed by frp, enabling Layer 3 connectivity between different clients within the frp network. Requires root/admin privileges and is currently supported on Linux and macOS. Configuration is done via the `virtualNet` section and the `virtual_net` plugin. Enable with feature gate `VirtualNet`. **Note: As an Alpha feature, configuration details may change in future releases.**

client/control.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
netpkg "github.com/fatedier/frp/pkg/util/net"
3030
"github.com/fatedier/frp/pkg/util/wait"
3131
"github.com/fatedier/frp/pkg/util/xlog"
32+
"github.com/fatedier/frp/pkg/vnet"
3233
)
3334

3435
type SessionContext struct {
@@ -46,6 +47,8 @@ type SessionContext struct {
4647
AuthSetter auth.Setter
4748
// Connector is used to create new connections, which could be real TCP connections or virtual streams.
4849
Connector Connector
50+
// Virtual net controller
51+
VnetController *vnet.Controller
4952
}
5053

5154
type Control struct {
@@ -99,8 +102,9 @@ func NewControl(ctx context.Context, sessionCtx *SessionContext) (*Control, erro
99102
ctl.registerMsgHandlers()
100103
ctl.msgTransporter = transport.NewMessageTransporter(ctl.msgDispatcher.SendChannel())
101104

102-
ctl.pm = proxy.NewManager(ctl.ctx, sessionCtx.Common, ctl.msgTransporter)
103-
ctl.vm = visitor.NewManager(ctl.ctx, sessionCtx.RunID, sessionCtx.Common, ctl.connectServer, ctl.msgTransporter)
105+
ctl.pm = proxy.NewManager(ctl.ctx, sessionCtx.Common, ctl.msgTransporter, sessionCtx.VnetController)
106+
ctl.vm = visitor.NewManager(ctl.ctx, sessionCtx.RunID, sessionCtx.Common,
107+
ctl.connectServer, ctl.msgTransporter, sessionCtx.VnetController)
104108
return ctl, nil
105109
}
106110

client/proxy/proxy.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/fatedier/frp/pkg/transport"
3737
"github.com/fatedier/frp/pkg/util/limit"
3838
"github.com/fatedier/frp/pkg/util/xlog"
39+
"github.com/fatedier/frp/pkg/vnet"
3940
)
4041

4142
var proxyFactoryRegistry = map[reflect.Type]func(*BaseProxy, v1.ProxyConfigurer) Proxy{}
@@ -58,6 +59,7 @@ func NewProxy(
5859
pxyConf v1.ProxyConfigurer,
5960
clientCfg *v1.ClientCommonConfig,
6061
msgTransporter transport.MessageTransporter,
62+
vnetController *vnet.Controller,
6163
) (pxy Proxy) {
6264
var limiter *rate.Limiter
6365
limitBytes := pxyConf.GetBaseConfig().Transport.BandwidthLimit.Bytes()
@@ -70,6 +72,7 @@ func NewProxy(
7072
clientCfg: clientCfg,
7173
limiter: limiter,
7274
msgTransporter: msgTransporter,
75+
vnetController: vnetController,
7376
xl: xlog.FromContextSafe(ctx),
7477
ctx: ctx,
7578
}
@@ -85,6 +88,7 @@ type BaseProxy struct {
8588
baseCfg *v1.ProxyBaseConfig
8689
clientCfg *v1.ClientCommonConfig
8790
msgTransporter transport.MessageTransporter
91+
vnetController *vnet.Controller
8892
limiter *rate.Limiter
8993
// proxyPlugin is used to handle connections instead of dialing to local service.
9094
// It's only validate for TCP protocol now.
@@ -98,7 +102,10 @@ type BaseProxy struct {
98102

99103
func (pxy *BaseProxy) Run() error {
100104
if pxy.baseCfg.Plugin.Type != "" {
101-
p, err := plugin.Create(pxy.baseCfg.Plugin.Type, pxy.baseCfg.Plugin.ClientPluginOptions)
105+
p, err := plugin.Create(pxy.baseCfg.Plugin.Type, plugin.PluginContext{
106+
Name: pxy.baseCfg.Name,
107+
VnetController: pxy.vnetController,
108+
}, pxy.baseCfg.Plugin.ClientPluginOptions)
102109
if err != nil {
103110
return err
104111
}
@@ -157,22 +164,22 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
157164
}
158165

159166
// check if we need to send proxy protocol info
160-
var extraInfo plugin.ExtraInfo
167+
var connInfo plugin.ConnectionInfo
161168
if m.SrcAddr != "" && m.SrcPort != 0 {
162169
if m.DstAddr == "" {
163170
m.DstAddr = "127.0.0.1"
164171
}
165172
srcAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.SrcAddr, strconv.Itoa(int(m.SrcPort))))
166173
dstAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.DstAddr, strconv.Itoa(int(m.DstPort))))
167-
extraInfo.SrcAddr = srcAddr
168-
extraInfo.DstAddr = dstAddr
174+
connInfo.SrcAddr = srcAddr
175+
connInfo.DstAddr = dstAddr
169176
}
170177

171178
if baseCfg.Transport.ProxyProtocolVersion != "" && m.SrcAddr != "" && m.SrcPort != 0 {
172179
h := &pp.Header{
173180
Command: pp.PROXY,
174-
SourceAddr: extraInfo.SrcAddr,
175-
DestinationAddr: extraInfo.DstAddr,
181+
SourceAddr: connInfo.SrcAddr,
182+
DestinationAddr: connInfo.DstAddr,
176183
}
177184

178185
if strings.Contains(m.SrcAddr, ".") {
@@ -186,13 +193,15 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
186193
} else if baseCfg.Transport.ProxyProtocolVersion == "v2" {
187194
h.Version = 2
188195
}
189-
extraInfo.ProxyProtocolHeader = h
196+
connInfo.ProxyProtocolHeader = h
190197
}
198+
connInfo.Conn = remote
199+
connInfo.UnderlyingConn = workConn
191200

192201
if pxy.proxyPlugin != nil {
193202
// if plugin is set, let plugin handle connection first
194203
xl.Debugf("handle by plugin: %s", pxy.proxyPlugin.Name())
195-
pxy.proxyPlugin.Handle(pxy.ctx, remote, workConn, &extraInfo)
204+
pxy.proxyPlugin.Handle(pxy.ctx, &connInfo)
196205
xl.Debugf("handle by plugin finished")
197206
return
198207
}
@@ -210,8 +219,8 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
210219
xl.Debugf("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(),
211220
localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String())
212221

213-
if extraInfo.ProxyProtocolHeader != nil {
214-
if _, err := extraInfo.ProxyProtocolHeader.WriteTo(localConn); err != nil {
222+
if connInfo.ProxyProtocolHeader != nil {
223+
if _, err := connInfo.ProxyProtocolHeader.WriteTo(localConn); err != nil {
215224
workConn.Close()
216225
xl.Errorf("write proxy protocol header to local conn error: %v", err)
217226
return

client/proxy/proxy_manager.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ import (
2828
"github.com/fatedier/frp/pkg/msg"
2929
"github.com/fatedier/frp/pkg/transport"
3030
"github.com/fatedier/frp/pkg/util/xlog"
31+
"github.com/fatedier/frp/pkg/vnet"
3132
)
3233

3334
type Manager struct {
3435
proxies map[string]*Wrapper
3536
msgTransporter transport.MessageTransporter
3637
inWorkConnCallback func(*v1.ProxyBaseConfig, net.Conn, *msg.StartWorkConn) bool
38+
vnetController *vnet.Controller
3739

3840
closed bool
3941
mu sync.RWMutex
@@ -47,10 +49,12 @@ func NewManager(
4749
ctx context.Context,
4850
clientCfg *v1.ClientCommonConfig,
4951
msgTransporter transport.MessageTransporter,
52+
vnetController *vnet.Controller,
5053
) *Manager {
5154
return &Manager{
5255
proxies: make(map[string]*Wrapper),
5356
msgTransporter: msgTransporter,
57+
vnetController: vnetController,
5458
closed: false,
5559
clientCfg: clientCfg,
5660
ctx: ctx,
@@ -159,7 +163,7 @@ func (pm *Manager) UpdateAll(proxyCfgs []v1.ProxyConfigurer) {
159163
for _, cfg := range proxyCfgs {
160164
name := cfg.GetBaseConfig().Name
161165
if _, ok := pm.proxies[name]; !ok {
162-
pxy := NewWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.msgTransporter)
166+
pxy := NewWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.msgTransporter, pm.vnetController)
163167
if pm.inWorkConnCallback != nil {
164168
pxy.SetInWorkConnCallback(pm.inWorkConnCallback)
165169
}

client/proxy/proxy_wrapper.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/fatedier/frp/pkg/msg"
3232
"github.com/fatedier/frp/pkg/transport"
3333
"github.com/fatedier/frp/pkg/util/xlog"
34+
"github.com/fatedier/frp/pkg/vnet"
3435
)
3536

3637
const (
@@ -73,6 +74,8 @@ type Wrapper struct {
7374
handler event.Handler
7475

7576
msgTransporter transport.MessageTransporter
77+
// vnet controller
78+
vnetController *vnet.Controller
7679

7780
health uint32
7881
lastSendStartMsg time.Time
@@ -91,6 +94,7 @@ func NewWrapper(
9194
clientCfg *v1.ClientCommonConfig,
9295
eventHandler event.Handler,
9396
msgTransporter transport.MessageTransporter,
97+
vnetController *vnet.Controller,
9498
) *Wrapper {
9599
baseInfo := cfg.GetBaseConfig()
96100
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(baseInfo.Name)
@@ -105,6 +109,7 @@ func NewWrapper(
105109
healthNotifyCh: make(chan struct{}),
106110
handler: eventHandler,
107111
msgTransporter: msgTransporter,
112+
vnetController: vnetController,
108113
xl: xl,
109114
ctx: xlog.NewContext(ctx, xl),
110115
}
@@ -117,7 +122,7 @@ func NewWrapper(
117122
xl.Tracef("enable health check monitor")
118123
}
119124

120-
pw.pxy = NewProxy(pw.ctx, pw.Cfg, clientCfg, pw.msgTransporter)
125+
pw.pxy = NewProxy(pw.ctx, pw.Cfg, clientCfg, pw.msgTransporter, pw.vnetController)
121126
return pw
122127
}
123128

client/service.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/fatedier/frp/pkg/util/version"
3838
"github.com/fatedier/frp/pkg/util/wait"
3939
"github.com/fatedier/frp/pkg/util/xlog"
40+
"github.com/fatedier/frp/pkg/vnet"
4041
)
4142

4243
func init() {
@@ -110,6 +111,8 @@ type Service struct {
110111
// web server for admin UI and apis
111112
webServer *httppkg.Server
112113

114+
vnetController *vnet.Controller
115+
113116
cfgMu sync.RWMutex
114117
common *v1.ClientCommonConfig
115118
proxyCfgs []v1.ProxyConfigurer
@@ -156,6 +159,9 @@ func NewService(options ServiceOptions) (*Service, error) {
156159
if webServer != nil {
157160
webServer.RouteRegister(s.registerRouteHandlers)
158161
}
162+
if options.Common.VirtualNet.Address != "" {
163+
s.vnetController = vnet.NewController(options.Common.VirtualNet)
164+
}
159165
return s, nil
160166
}
161167

@@ -169,6 +175,19 @@ func (svr *Service) Run(ctx context.Context) error {
169175
netpkg.SetDefaultDNSAddress(svr.common.DNSServer)
170176
}
171177

178+
if svr.vnetController != nil {
179+
if err := svr.vnetController.Init(); err != nil {
180+
log.Errorf("init virtual network controller error: %v", err)
181+
return err
182+
}
183+
go func() {
184+
log.Infof("virtual network controller start...")
185+
if err := svr.vnetController.Run(); err != nil {
186+
log.Warnf("virtual network controller exit with error: %v", err)
187+
}
188+
}()
189+
}
190+
172191
if svr.webServer != nil {
173192
go func() {
174193
log.Infof("admin server listen on %s", svr.webServer.Address())
@@ -311,12 +330,13 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
311330
connEncrypted = false
312331
}
313332
sessionCtx := &SessionContext{
314-
Common: svr.common,
315-
RunID: svr.runID,
316-
Conn: conn,
317-
ConnEncrypted: connEncrypted,
318-
AuthSetter: svr.authSetter,
319-
Connector: connector,
333+
Common: svr.common,
334+
RunID: svr.runID,
335+
Conn: conn,
336+
ConnEncrypted: connEncrypted,
337+
AuthSetter: svr.authSetter,
338+
Connector: connector,
339+
VnetController: svr.vnetController,
320340
}
321341
ctl, err := NewControl(svr.ctx, sessionCtx)
322342
if err != nil {

client/visitor/stcp.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ func (sv *STCPVisitor) Run() (err error) {
4444
}
4545

4646
go sv.internalConnWorker()
47+
48+
if sv.plugin != nil {
49+
sv.plugin.Start()
50+
}
4751
return
4852
}
4953

0 commit comments

Comments
 (0)