1
0

virtual_net.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Copyright 2025 The frp Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //go:build !frps
  15. package visitor
  16. import (
  17. "context"
  18. "errors"
  19. "fmt"
  20. "net"
  21. "sync"
  22. "time"
  23. v1 "github.com/fatedier/frp/pkg/config/v1"
  24. netutil "github.com/fatedier/frp/pkg/util/net"
  25. "github.com/fatedier/frp/pkg/util/xlog"
  26. )
  27. func init() {
  28. Register(v1.VisitorPluginVirtualNet, NewVirtualNetPlugin)
  29. }
  30. type VirtualNetPlugin struct {
  31. pluginCtx PluginContext
  32. routes []net.IPNet
  33. mu sync.Mutex
  34. controllerConn net.Conn
  35. closeSignal chan struct{}
  36. ctx context.Context
  37. cancel context.CancelFunc
  38. }
  39. func NewVirtualNetPlugin(pluginCtx PluginContext, options v1.VisitorPluginOptions) (Plugin, error) {
  40. opts := options.(*v1.VirtualNetVisitorPluginOptions)
  41. p := &VirtualNetPlugin{
  42. pluginCtx: pluginCtx,
  43. routes: make([]net.IPNet, 0),
  44. }
  45. p.ctx, p.cancel = context.WithCancel(pluginCtx.Ctx)
  46. if opts.DestinationIP == "" {
  47. return nil, errors.New("destinationIP is required")
  48. }
  49. // Parse DestinationIP and create a host route.
  50. ip := net.ParseIP(opts.DestinationIP)
  51. if ip == nil {
  52. return nil, fmt.Errorf("invalid destination IP address [%s]", opts.DestinationIP)
  53. }
  54. var mask net.IPMask
  55. if ip.To4() != nil {
  56. mask = net.CIDRMask(32, 32) // /32 for IPv4
  57. } else {
  58. mask = net.CIDRMask(128, 128) // /128 for IPv6
  59. }
  60. p.routes = append(p.routes, net.IPNet{IP: ip, Mask: mask})
  61. return p, nil
  62. }
  63. func (p *VirtualNetPlugin) Name() string {
  64. return v1.VisitorPluginVirtualNet
  65. }
  66. func (p *VirtualNetPlugin) Start() {
  67. xl := xlog.FromContextSafe(p.pluginCtx.Ctx)
  68. if p.pluginCtx.VnetController == nil {
  69. return
  70. }
  71. routeStr := "unknown"
  72. if len(p.routes) > 0 {
  73. routeStr = p.routes[0].String()
  74. }
  75. xl.Infof("starting VirtualNetPlugin for visitor [%s], attempting to register routes for %s", p.pluginCtx.Name, routeStr)
  76. go p.run()
  77. }
  78. func (p *VirtualNetPlugin) run() {
  79. xl := xlog.FromContextSafe(p.ctx)
  80. reconnectDelay := 10 * time.Second
  81. for {
  82. currentCloseSignal := make(chan struct{})
  83. p.mu.Lock()
  84. p.closeSignal = currentCloseSignal
  85. p.mu.Unlock()
  86. select {
  87. case <-p.ctx.Done():
  88. xl.Infof("VirtualNetPlugin run loop for visitor [%s] stopping (context cancelled before pipe creation).", p.pluginCtx.Name)
  89. p.cleanupControllerConn(xl)
  90. return
  91. default:
  92. }
  93. controllerConn, pluginConn := net.Pipe()
  94. p.mu.Lock()
  95. p.controllerConn = controllerConn
  96. p.mu.Unlock()
  97. pluginNotifyConn := netutil.WrapCloseNotifyConn(pluginConn, func() {
  98. close(currentCloseSignal) // Signal the run loop on close.
  99. })
  100. xl.Infof("attempting to register client route for visitor [%s]", p.pluginCtx.Name)
  101. p.pluginCtx.VnetController.RegisterClientRoute(p.ctx, p.pluginCtx.Name, p.routes, controllerConn)
  102. xl.Infof("successfully registered client route for visitor [%s]. Starting connection handler with CloseNotifyConn.", p.pluginCtx.Name)
  103. // Pass the CloseNotifyConn to HandleConn.
  104. // HandleConn is responsible for calling Close() on pluginNotifyConn.
  105. p.pluginCtx.HandleConn(pluginNotifyConn)
  106. // Wait for context cancellation or connection close.
  107. select {
  108. case <-p.ctx.Done():
  109. xl.Infof("VirtualNetPlugin run loop stopping for visitor [%s] (context cancelled while waiting).", p.pluginCtx.Name)
  110. p.cleanupControllerConn(xl)
  111. return
  112. case <-currentCloseSignal:
  113. xl.Infof("detected connection closed via CloseNotifyConn for visitor [%s].", p.pluginCtx.Name)
  114. // HandleConn closed the plugin side. Close the controller side.
  115. p.cleanupControllerConn(xl)
  116. xl.Infof("waiting %v before attempting reconnection for visitor [%s]...", reconnectDelay, p.pluginCtx.Name)
  117. select {
  118. case <-time.After(reconnectDelay):
  119. case <-p.ctx.Done():
  120. xl.Infof("VirtualNetPlugin reconnection delay interrupted for visitor [%s]", p.pluginCtx.Name)
  121. return
  122. }
  123. }
  124. xl.Infof("re-establishing virtual connection for visitor [%s]...", p.pluginCtx.Name)
  125. }
  126. }
  127. // cleanupControllerConn closes the current controllerConn (if it exists) under lock.
  128. func (p *VirtualNetPlugin) cleanupControllerConn(xl *xlog.Logger) {
  129. p.mu.Lock()
  130. defer p.mu.Unlock()
  131. if p.controllerConn != nil {
  132. xl.Debugf("cleaning up controllerConn for visitor [%s]", p.pluginCtx.Name)
  133. p.controllerConn.Close()
  134. p.controllerConn = nil
  135. }
  136. p.closeSignal = nil
  137. }
  138. // Close initiates the plugin shutdown.
  139. func (p *VirtualNetPlugin) Close() error {
  140. xl := xlog.FromContextSafe(p.pluginCtx.Ctx)
  141. xl.Infof("closing VirtualNetPlugin for visitor [%s]", p.pluginCtx.Name)
  142. // Signal the run loop goroutine to stop.
  143. p.cancel()
  144. // Unregister the route from the controller.
  145. if p.pluginCtx.VnetController != nil {
  146. p.pluginCtx.VnetController.UnregisterClientRoute(p.pluginCtx.Name)
  147. xl.Infof("unregistered client route for visitor [%s]", p.pluginCtx.Name)
  148. }
  149. // Explicitly close the controller side of the pipe.
  150. // This ensures the pipe is broken even if the run loop is stuck or HandleConn hasn't closed its end.
  151. p.cleanupControllerConn(xl)
  152. xl.Infof("finished cleaning up connections during close for visitor [%s]", p.pluginCtx.Name)
  153. return nil
  154. }