// 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 vnet import ( "fmt" "io" "net" "os/exec" "strings" ) const ( defaultTunName = "utun" defaultMTU = 1350 ) func createTun(addr string) (io.ReadWriteCloser, string, net.IP, error) { ifce, name, err := createTunDevice(defaultTunName, defaultMTU) if err != nil { return nil, "", nil, err } ip, ipNet, err := net.ParseCIDR(addr) if err != nil { return nil, "", nil, err } // Calculate a peer IP for the point-to-point tunnel peerIP := generatePeerIP(ip) // Configure the interface with proper point-to-point addressing cmd := fmt.Sprintf("ifconfig %s inet %s %s mtu %d up", name, ip.String(), peerIP.String(), defaultMTU) args := strings.Split(cmd, " ") if err = exec.Command(args[0], args[1:]...).Run(); err != nil { return nil, "", nil, err } // Add default route for the tunnel subnet routes := []net.IPNet{*ipNet} if err = addRoutes(name, routes); err != nil { return nil, "", nil, err } return ifce, name, ip, nil } // generatePeerIP creates a peer IP for the point-to-point tunnel // by incrementing the last octet of the IP func generatePeerIP(ip net.IP) net.IP { // Make a copy to avoid modifying the original peerIP := make(net.IP, len(ip)) copy(peerIP, ip) // Increment the last octet peerIP[len(peerIP)-1]++ return peerIP } // addRoutes configures system routes for the TUN interface func addRoutes(ifName string, routes []net.IPNet) error { for _, route := range routes { cmd := fmt.Sprintf("route add -net %s -interface %s", route.String(), ifName) args := strings.Split(cmd, " ") if err := exec.Command(args[0], args[1:]...).Run(); err != nil { return err } } return nil }