// 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. package main import ( "flag" "fmt" "net" "os" "os/signal" "syscall" "time" v1 "github.com/fatedier/frp/pkg/config/v1" "github.com/fatedier/frp/pkg/vnet" ) func main() { // Parse command-line flags serverAddr := flag.String("server", "localhost:8000", "Server address (ip:port)") clientTUNAddr := flag.String("tun", "10.10.0.1/24", "Client TUN device address") serverTUNNet := flag.String("server-net", "10.20.0.0/24", "Server TUN network for routing") flag.Parse() fmt.Printf("Connecting to server at %s\n", *serverAddr) fmt.Printf("Client TUN address: %s\n", *clientTUNAddr) fmt.Printf("Server network route: %s\n", *serverTUNNet) // Set up vnet controller on the client clientCfg := v1.VirtualNetConfig{ Address: *clientTUNAddr, Routes: []string{*serverTUNNet}, } clientController := vnet.NewController(clientCfg) if err := clientController.Init(); err != nil { fmt.Printf("Client init error: %v\n", err) return } // Handle shutdown gracefully c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) go func() { <-c fmt.Println("\nShutting down client...") os.Exit(0) }() // Start the controller go func() { if err := clientController.Run(); err != nil { fmt.Printf("Client run error: %v\n", err) } }() // Main connection loop for { // Connect to the server conn, err := net.Dial("tcp", *serverAddr) if err != nil { fmt.Printf("Failed to connect to server %s: %v\n", *serverAddr, err) time.Sleep(5 * time.Second) continue } fmt.Printf("Connected to server at %s\n", conn.RemoteAddr()) // Wrap the connection rwc := &readWriteCloserConn{conn} // Parse routes from config clientRoutes, err := vnet.ParseRoutes(clientCfg.Routes) if err != nil { fmt.Printf("Parse client routes error: %v\n", err) conn.Close() return } // Register the route with the connection if err := clientController.RegisterClientRoute("server", clientRoutes, rwc); err != nil { fmt.Printf("Register client route error: %v\n", err) conn.Close() time.Sleep(5 * time.Second) continue } fmt.Println("VPN tunnel established with server") fmt.Printf("Client TUN: %s, routes: %s\n", *clientTUNAddr, *serverTUNNet) time.Sleep(60 * time.Second) } } // readWriteCloserConn wraps net.Conn as io.ReadWriteCloser type readWriteCloserConn struct { net.Conn } func (rwc *readWriteCloserConn) Read(p []byte) (n int, err error) { return rwc.Conn.Read(p) } func (rwc *readWriteCloserConn) Write(p []byte) (n int, err error) { return rwc.Conn.Write(p) } func (rwc *readWriteCloserConn) Close() error { return rwc.Conn.Close() }