mirror of
https://github.com/v2rayA/v2rayA.git
synced 2026-10-22 01:06:18 +08:00
1dd2bf28ad
transparentType=tun no longer starts the TinyTun process. The template emits a tun-mips inbound tagged transparent — device name, address, MTU, the DNS module's address as the relay target, the user's exclusion list plus v2rayA and the core by name and v2rayA by PID — and the service does what the operating system needs once the core has the device up. On Linux that is one table and four policy rules: table 2020 holds the default route into tun0; marked packets (0x80: the core's sockets, the direct outbound, the DNS module's upstreams) look up main in full and leave through the physical interface, or fail with ENETUNREACH when main has no route for their family rather than fall into the TUN; unmarked packets take main with its default route suppressed, so LAN and static routes stay physical, and everything else goes to table 2020. The main table is never edited, so stopping restores egress by itself. A rule in our preference range or a route in our table that is not ours is reported and left alone; our own leftovers from a crash are removed first. Every command that succeeded is recorded and undone in reverse on stop. Three things the TinyTun path never hit are now on tun's path, so they are fixed here: the DNS module is generated in tun mode (TinyTun did DNS itself); its 127.2.0.17:53 listener is only added when port 53 is free, instead of making the core exit on a host running another resolver; its primary listener stays on loopback unless port sharing is on or the user chose an address, and the DNS REDIRECT rules and their cleanup follow the module's port instead of a hard-coded 52353, and a ::1 listener is added when IPv6 is supported, because ip6tables REDIRECT delivers an application's IPv6 query to ::1 which the IPv4 listener never saw. The tun branch no longer skips those rules: they are what catches queries to a LAN resolver, which the routing keeps off the TUN. The connectivity monitor probes the core's API port; the TinyTun socks loop-back port it used is gone. Removed with TinyTun: --tinytun-bin, the tunProcessBackend (eBPF) and tunBypassInterfaces settings (connected and static routes bypass the TUN unconditionally now), the /networkInterfaces endpoint that only served the latter, and the build tag. The version endpoint reports tunSupported instead of tinytunSupported. Tests cover the generated inbound and DNS module, the listen-address rules, the redirect port, and — with V2RAYA_CORE_BIN set — that the generated document loads in the real core. Windows and macOS route handling follows in a separate commit; until then those builds report the TUN as unsupported.
177 lines
4.2 KiB
Go
177 lines
4.2 KiB
Go
package iptables
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os/exec"
|
|
"regexp"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/v2rayA/v2rayA/common"
|
|
"github.com/v2rayA/v2rayA/common/cmds"
|
|
"github.com/v2rayA/v2rayA/common/parseGeoIP"
|
|
"github.com/v2rayA/v2rayA/conf"
|
|
"github.com/v2rayA/v2rayA/db/configure"
|
|
"golang.org/x/net/nettest"
|
|
)
|
|
|
|
func IPNet2CIDR(ipnet *net.IPNet) string {
|
|
ones, _ := ipnet.Mask.Size()
|
|
return ipnet.IP.String() + "/" + strconv.Itoa(ones)
|
|
}
|
|
|
|
func GetLocalCIDR() ([]string, error) {
|
|
addrs, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var cidrs []string
|
|
for _, addr := range addrs {
|
|
if ipnet, ok := addr.(*net.IPNet); ok {
|
|
cidrs = append(cidrs, IPNet2CIDR(ipnet))
|
|
}
|
|
}
|
|
return cidrs, nil
|
|
}
|
|
|
|
func IsNft() bool {
|
|
if _, isNft := Redirect.(*nftRedirect); isNft {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func IsIPv6Supported() bool {
|
|
switch conf.GetEnvironmentConfig().IPV6Support {
|
|
case "on":
|
|
return true
|
|
case "off":
|
|
return false
|
|
default:
|
|
}
|
|
if common.IsDocker() {
|
|
return false
|
|
}
|
|
if !nettest.SupportsIPv6() {
|
|
return false
|
|
} else if runtime.GOOS != "linux" {
|
|
return true
|
|
}
|
|
if IsNft() {
|
|
return true
|
|
}
|
|
return cmds.IsCommandValid("ip6tables") || cmds.IsCommandValid("ip6tables-nft")
|
|
}
|
|
|
|
func IsNftablesSupported() bool {
|
|
if runtime.GOOS != "linux" {
|
|
return false
|
|
}
|
|
switch conf.GetEnvironmentConfig().NftablesSupport {
|
|
// Warning:
|
|
// This is an experimental feature for nftables support.
|
|
// The default value is "off" for now but may be changed to "auto" in the future
|
|
case "on":
|
|
return true
|
|
case "off":
|
|
return false
|
|
default:
|
|
}
|
|
if common.IsDocker() {
|
|
return false
|
|
}
|
|
if !cmds.IsCommandValid("nft") {
|
|
// No nft.
|
|
return false
|
|
}
|
|
out, err := exec.Command("iptables", "--version").Output()
|
|
if err != nil {
|
|
// No iptables.
|
|
return true
|
|
}
|
|
return strings.Contains(string(out), "nf_tables")
|
|
}
|
|
|
|
func GetWhiteListIPs() ([]string, []string, error) {
|
|
dataModal := configure.GetTproxyWhiteIpGroups()
|
|
|
|
var ipv4List []string
|
|
var ipv6List []string
|
|
for _, cc := range dataModal.CountryCodes {
|
|
ipv4s, ipv6s, _ := parseGeoIP.Parser("geoip.dat", cc)
|
|
ipv4List = append(ipv4List, ipv4s...)
|
|
ipv6List = append(ipv6List, ipv6s...)
|
|
}
|
|
for _, v := range dataModal.CustomIps {
|
|
// The GUI submits the textarea lines as typed, so a pasted CIDR can
|
|
// carry surrounding whitespace that net.ParseCIDR does not accept.
|
|
v = strings.TrimSpace(v)
|
|
if v == "" {
|
|
continue
|
|
}
|
|
if net.ParseIP(v) == nil {
|
|
if _, _, err := net.ParseCIDR(v); err != nil {
|
|
return nil, nil, fmt.Errorf("invalid tproxy whitelist IP/CIDR %q", v)
|
|
}
|
|
}
|
|
if strings.Contains(v, ":") {
|
|
ipv6List = append(ipv6List, v)
|
|
} else {
|
|
ipv4List = append(ipv4List, v)
|
|
}
|
|
}
|
|
return ipv4List, ipv6List, nil
|
|
}
|
|
|
|
func IsEnabledTproxyWhiteIpGroups() bool {
|
|
ipv4List, ipv6List, err := GetWhiteListIPs()
|
|
return err == nil && (len(ipv4List) > 0 || len(ipv6List) > 0)
|
|
}
|
|
|
|
// interface names plus the "*" wildcard users write for docker*/veth*
|
|
var interfaceNameRegexp = regexp.MustCompile(`^[A-Za-z0-9_.:*+-]+$`)
|
|
|
|
func GetExcludedInterfaces() ([]string, error) {
|
|
setting := configure.GetSettingNotNil()
|
|
if setting.TproxyExcludedInterfaces == "" {
|
|
return []string{}, nil
|
|
}
|
|
ifs := strings.Split(setting.TproxyExcludedInterfaces, ",")
|
|
var res []string
|
|
for _, v := range ifs {
|
|
v = strings.TrimSpace(v)
|
|
if v != "" {
|
|
if !interfaceNameRegexp.MatchString(v) {
|
|
return nil, fmt.Errorf("invalid tproxy excluded interface %q", v)
|
|
}
|
|
res = append(res, v)
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func getTproxySetupValues() (interfaces, ipv4List, ipv6List []string, err error) {
|
|
if interfaces, err = GetExcludedInterfaces(); err != nil {
|
|
return
|
|
}
|
|
ipv4List, ipv6List, err = GetWhiteListIPs()
|
|
return
|
|
}
|
|
|
|
// DnsModulePort is the port the DNS module listens on: the rule sets are
|
|
// written for the historical 52353 and rewritten to the configured port
|
|
// when it differs, so the TPROXY and REDIRECT targets, the loop-back RETURN
|
|
// and the module's listener always agree.
|
|
func DnsModulePort() string {
|
|
return configure.GetSettingNotNil().DnsModulePort()
|
|
}
|
|
|
|
func withDnsModulePort(rules string) string {
|
|
if port := DnsModulePort(); port != "52353" {
|
|
return strings.ReplaceAll(rules, "52353", port)
|
|
}
|
|
return rules
|
|
}
|