mirror of
https://github.com/SagerNet/sing-tun.git
synced 2025-04-05 12:57:39 +03:00
Fix linux route
This commit is contained in:
parent
9dc73c0bcc
commit
9968d2c8e9
5 changed files with 489 additions and 103 deletions
52
monitor_android.go
Normal file
52
monitor_android.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||||
|
ruleList, err := netlink.RuleList(netlink.FAMILY_ALL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultTableIndex int
|
||||||
|
for _, rule := range ruleList {
|
||||||
|
if rule.Mask == 0xFFFF {
|
||||||
|
defaultTableIndex = rule.Table
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if defaultTableIndex == 0 {
|
||||||
|
return E.New("no route to internet")
|
||||||
|
}
|
||||||
|
|
||||||
|
routes, err := netlink.RouteListFiltered(netlink.FAMILY_ALL, &netlink.Route{Table: defaultTableIndex}, netlink.RT_FILTER_TABLE)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range routes {
|
||||||
|
var link netlink.Link
|
||||||
|
link, err = netlink.LinkByIndex(route.LinkIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldInterface := m.defaultInterfaceName
|
||||||
|
oldIndex := m.defaultInterfaceIndex
|
||||||
|
|
||||||
|
m.defaultInterfaceName = link.Attrs().Name
|
||||||
|
m.defaultInterfaceIndex = link.Attrs().Index
|
||||||
|
|
||||||
|
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m.callback()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return E.New("no route in the system table")
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -122,41 +121,6 @@ func (m *defaultInterfaceMonitor) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *defaultInterfaceMonitor) checkUpdate() error {
|
|
||||||
routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, route := range routes {
|
|
||||||
var link netlink.Link
|
|
||||||
link, err = netlink.LinkByIndex(route.LinkIndex)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if link.Attrs().Flags&net.FlagUp == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if link.Type() == "tuntap" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
oldInterface := m.defaultInterfaceName
|
|
||||||
oldIndex := m.defaultInterfaceIndex
|
|
||||||
|
|
||||||
m.defaultInterfaceName = link.Attrs().Name
|
|
||||||
m.defaultInterfaceIndex = link.Attrs().Index
|
|
||||||
|
|
||||||
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
m.callback()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return E.New("no route to internet")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *defaultInterfaceMonitor) DefaultInterfaceName() string {
|
func (m *defaultInterfaceMonitor) DefaultInterfaceName() string {
|
||||||
return m.defaultInterfaceName
|
return m.defaultInterfaceName
|
||||||
}
|
}
|
||||||
|
|
41
monitor_linux_default.go
Normal file
41
monitor_linux_default.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//go:build linux && !android
|
||||||
|
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *defaultInterfaceMonitor) checkUpdate() error {
|
||||||
|
routes, err := netlink.RouteListFiltered(netlink.FAMILY_ALL, &netlink.Route{Table: unix.RT_TABLE_MAIN}, netlink.RT_FILTER_TABLE)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, route := range routes {
|
||||||
|
if route.Dst != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var link netlink.Link
|
||||||
|
link, err = netlink.LinkByIndex(route.LinkIndex)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldInterface := m.defaultInterfaceName
|
||||||
|
oldIndex := m.defaultInterfaceIndex
|
||||||
|
|
||||||
|
m.defaultInterfaceName = link.Attrs().Name
|
||||||
|
m.defaultInterfaceIndex = link.Attrs().Index
|
||||||
|
|
||||||
|
if oldInterface == m.defaultInterfaceName && oldIndex == m.defaultInterfaceIndex {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m.callback()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return E.New("no route to internet")
|
||||||
|
}
|
213
netlink_linux.go
Normal file
213
netlink_linux.go
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
package tun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
_ "unsafe"
|
||||||
|
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"github.com/vishvananda/netlink/nl"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Rule struct {
|
||||||
|
Priority int
|
||||||
|
Family int
|
||||||
|
Table int
|
||||||
|
Mark int
|
||||||
|
Mask int
|
||||||
|
TunID uint
|
||||||
|
Goto int
|
||||||
|
Src netip.Prefix
|
||||||
|
Dst netip.Prefix
|
||||||
|
Flow int
|
||||||
|
IifName string
|
||||||
|
OifName string
|
||||||
|
SuppressIfgroup int
|
||||||
|
SuppressPrefixLength int
|
||||||
|
Invert bool
|
||||||
|
|
||||||
|
IPProtocol int
|
||||||
|
SrcPortRange *RulePortRange
|
||||||
|
DstPortRange *RulePortRange
|
||||||
|
UIDRange *RuleUIDRange
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRule() *Rule {
|
||||||
|
return &Rule{
|
||||||
|
SuppressIfgroup: -1,
|
||||||
|
SuppressPrefixLength: -1,
|
||||||
|
Priority: -1,
|
||||||
|
Mark: -1,
|
||||||
|
Mask: -1,
|
||||||
|
Goto: -1,
|
||||||
|
Flow: -1,
|
||||||
|
IPProtocol: -1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname pkgHandle github.com/vishvananda/netlink.pkgHandle
|
||||||
|
var pkgHandle *netlink.Handle
|
||||||
|
|
||||||
|
//go:linkname newNetlinkRequest github.com/vishvananda/netlink.(*Handle).newNetlinkRequest
|
||||||
|
func newNetlinkRequest(h *netlink.Handle, proto, flags int) *nl.NetlinkRequest
|
||||||
|
|
||||||
|
func RuleAdd(rule *Rule) error {
|
||||||
|
req := newNetlinkRequest(pkgHandle, unix.RTM_NEWRULE, unix.NLM_F_CREATE|unix.NLM_F_EXCL|unix.NLM_F_ACK)
|
||||||
|
return ruleHandle(rule, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RuleDel(rule *Rule) error {
|
||||||
|
req := newNetlinkRequest(pkgHandle, unix.RTM_DELRULE, unix.NLM_F_ACK)
|
||||||
|
return ruleHandle(rule, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RulePortRange struct {
|
||||||
|
Start uint16
|
||||||
|
End uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *RulePortRange) toRtAttrData() []byte {
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
b := [][]byte{make([]byte, 2), make([]byte, 2)}
|
||||||
|
native.PutUint16(b[0], pr.Start)
|
||||||
|
native.PutUint16(b[1], pr.End)
|
||||||
|
return bytes.Join(b, []byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleUIDRange struct {
|
||||||
|
Start uint32
|
||||||
|
End uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *RuleUIDRange) toRtAttrData() []byte {
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
b := [][]byte{make([]byte, 4), make([]byte, 4)}
|
||||||
|
native.PutUint32(b[0], pr.Start)
|
||||||
|
native.PutUint32(b[1], pr.End)
|
||||||
|
return bytes.Join(b, []byte{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func ruleHandle(rule *Rule, req *nl.NetlinkRequest) error {
|
||||||
|
msg := nl.NewRtMsg()
|
||||||
|
msg.Family = unix.AF_INET
|
||||||
|
msg.Protocol = unix.RTPROT_BOOT
|
||||||
|
msg.Scope = unix.RT_SCOPE_UNIVERSE
|
||||||
|
msg.Table = unix.RT_TABLE_UNSPEC
|
||||||
|
msg.Type = unix.RTN_UNSPEC
|
||||||
|
if rule.Table >= 256 {
|
||||||
|
msg.Type = unix.FR_ACT_TO_TBL
|
||||||
|
} else if rule.Goto >= 0 {
|
||||||
|
msg.Type = unix.FR_ACT_GOTO
|
||||||
|
} else if req.NlMsghdr.Flags&unix.NLM_F_CREATE > 0 {
|
||||||
|
msg.Type = unix.FR_ACT_NOP
|
||||||
|
}
|
||||||
|
if rule.Invert {
|
||||||
|
msg.Flags |= netlink.FibRuleInvert
|
||||||
|
}
|
||||||
|
if rule.Family != 0 {
|
||||||
|
msg.Family = uint8(rule.Family)
|
||||||
|
}
|
||||||
|
if rule.Table >= 0 && rule.Table < 256 {
|
||||||
|
msg.Table = uint8(rule.Table)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dstFamily uint8
|
||||||
|
var rtAttrs []*nl.RtAttr
|
||||||
|
|
||||||
|
if rule.Dst.IsValid() {
|
||||||
|
msg.Dst_len = uint8(rule.Dst.Bits())
|
||||||
|
msg.Family = uint8(nl.GetIPFamily(rule.Dst.Addr().AsSlice()))
|
||||||
|
dstFamily = msg.Family
|
||||||
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_DST, rule.Dst.Addr().AsSlice()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if rule.Src.IsValid() {
|
||||||
|
msg.Src_len = uint8(rule.Src.Bits())
|
||||||
|
msg.Family = uint8(nl.GetIPFamily(rule.Src.Addr().AsSlice()))
|
||||||
|
if dstFamily != 0 && dstFamily != msg.Family {
|
||||||
|
return fmt.Errorf("source and destination ip are not the same IP family")
|
||||||
|
}
|
||||||
|
dstFamily = msg.Family
|
||||||
|
rtAttrs = append(rtAttrs, nl.NewRtAttr(unix.RTA_SRC, rule.Src.Addr().AsSlice()))
|
||||||
|
}
|
||||||
|
|
||||||
|
req.AddData(msg)
|
||||||
|
for i := range rtAttrs {
|
||||||
|
req.AddData(rtAttrs[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
native := nl.NativeEndian()
|
||||||
|
|
||||||
|
if rule.Priority >= 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.Priority))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_PRIORITY, b))
|
||||||
|
}
|
||||||
|
if rule.Mark >= 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.Mark))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_FWMARK, b))
|
||||||
|
}
|
||||||
|
if rule.Mask >= 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.Mask))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_FWMASK, b))
|
||||||
|
}
|
||||||
|
if rule.Flow >= 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.Flow))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_FLOW, b))
|
||||||
|
}
|
||||||
|
if rule.TunID > 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.TunID))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_TUN_ID, b))
|
||||||
|
}
|
||||||
|
if rule.Table >= 256 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.Table))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_TABLE, b))
|
||||||
|
}
|
||||||
|
if msg.Table > 0 {
|
||||||
|
if rule.SuppressPrefixLength >= 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.SuppressPrefixLength))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_PREFIXLEN, b))
|
||||||
|
}
|
||||||
|
if rule.SuppressIfgroup >= 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.SuppressIfgroup))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_SUPPRESS_IFGROUP, b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rule.IifName != "" {
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_IIFNAME, []byte(rule.IifName)))
|
||||||
|
}
|
||||||
|
if rule.OifName != "" {
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_OIFNAME, []byte(rule.OifName)))
|
||||||
|
}
|
||||||
|
if rule.Goto >= 0 {
|
||||||
|
b := make([]byte, 4)
|
||||||
|
native.PutUint32(b, uint32(rule.Goto))
|
||||||
|
req.AddData(nl.NewRtAttr(nl.FRA_GOTO, b))
|
||||||
|
}
|
||||||
|
if rule.IPProtocol >= 0 {
|
||||||
|
req.AddData(nl.NewRtAttr(unix.FRA_IP_PROTO, []byte{byte(rule.IPProtocol)}))
|
||||||
|
}
|
||||||
|
if rule.SrcPortRange != nil {
|
||||||
|
b := rule.SrcPortRange.toRtAttrData()
|
||||||
|
req.AddData(nl.NewRtAttr(unix.FRA_SPORT_RANGE, b))
|
||||||
|
}
|
||||||
|
if rule.DstPortRange != nil {
|
||||||
|
b := rule.DstPortRange.toRtAttrData()
|
||||||
|
req.AddData(nl.NewRtAttr(unix.FRA_DPORT_RANGE, b))
|
||||||
|
}
|
||||||
|
if rule.UIDRange != nil {
|
||||||
|
b := rule.UIDRange.toRtAttrData()
|
||||||
|
req.AddData(nl.NewRtAttr(unix.FRA_UID_RANGE, b))
|
||||||
|
}
|
||||||
|
_, err := req.Execute(unix.NETLINK_ROUTE, 0)
|
||||||
|
return err
|
||||||
|
}
|
250
tun_linux.go
250
tun_linux.go
|
@ -1,16 +1,13 @@
|
||||||
package tun
|
package tun
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"runtime"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
|
"gvisor.dev/gvisor/pkg/tcpip/link/tun"
|
||||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||||
|
@ -18,11 +15,11 @@ import (
|
||||||
|
|
||||||
type NativeTun struct {
|
type NativeTun struct {
|
||||||
name string
|
name string
|
||||||
|
fd int
|
||||||
inet4Address netip.Prefix
|
inet4Address netip.Prefix
|
||||||
inet6Address netip.Prefix
|
inet6Address netip.Prefix
|
||||||
mtu uint32
|
mtu uint32
|
||||||
autoRoute bool
|
autoRoute bool
|
||||||
fdList []int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) (Tun, error) {
|
func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu uint32, autoRoute bool) (Tun, error) {
|
||||||
|
@ -30,49 +27,31 @@ func Open(name string, inet4Address netip.Prefix, inet6Address netip.Prefix, mtu
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
tunLink, err := netlink.LinkByName(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Errors(err, unix.Close(tunFd))
|
||||||
|
}
|
||||||
nativeTun := &NativeTun{
|
nativeTun := &NativeTun{
|
||||||
name: name,
|
name: name,
|
||||||
fdList: []int{tunFd},
|
fd: tunFd,
|
||||||
mtu: mtu,
|
mtu: mtu,
|
||||||
inet4Address: inet4Address,
|
inet4Address: inet4Address,
|
||||||
inet6Address: inet6Address,
|
inet6Address: inet6Address,
|
||||||
autoRoute: autoRoute,
|
autoRoute: autoRoute,
|
||||||
}
|
}
|
||||||
err = nativeTun.configure()
|
err = nativeTun.configure(tunLink)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Errors(err, syscall.Close(tunFd))
|
return nil, E.Errors(err, unix.Close(tunFd))
|
||||||
}
|
}
|
||||||
return nativeTun, nil
|
return nativeTun, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *NativeTun) routes(tunLink netlink.Link) []netlink.Route {
|
func (t *NativeTun) configure(tunLink netlink.Link) error {
|
||||||
var routes []netlink.Route
|
err := netlink.LinkSetMTU(tunLink, int(t.mtu))
|
||||||
if t.inet4Address.IsValid() {
|
|
||||||
routes = append(routes, netlink.Route{
|
|
||||||
Dst: &net.IPNet{
|
|
||||||
IP: net.IPv4zero,
|
|
||||||
Mask: net.CIDRMask(0, 32),
|
|
||||||
},
|
|
||||||
LinkIndex: tunLink.Attrs().Index,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if t.inet6Address.IsValid() {
|
|
||||||
routes = append(routes, netlink.Route{
|
|
||||||
Dst: &net.IPNet{
|
|
||||||
IP: net.IPv6zero,
|
|
||||||
Mask: net.CIDRMask(0, 128),
|
|
||||||
},
|
|
||||||
LinkIndex: tunLink.Attrs().Index,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return routes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *NativeTun) configure() error {
|
|
||||||
tunLink, err := netlink.LinkByName(t.name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.inet4Address.IsValid() {
|
if t.inet4Address.IsValid() {
|
||||||
addr4, _ := netlink.ParseAddr(t.inet4Address.String())
|
addr4, _ := netlink.ParseAddr(t.inet4Address.String())
|
||||||
err = netlink.AddrAdd(tunLink, addr4)
|
err = netlink.AddrAdd(tunLink, addr4)
|
||||||
|
@ -89,61 +68,198 @@ func (t *NativeTun) configure() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = netlink.LinkSetMTU(tunLink, int(t.mtu))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = netlink.LinkSetUp(tunLink)
|
err = netlink.LinkSetUp(tunLink)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.autoRoute {
|
if t.autoRoute {
|
||||||
for _, route := range t.routes(tunLink) {
|
err = t.setRoute(tunLink)
|
||||||
err = netlink.RouteAdd(&route)
|
if err != nil {
|
||||||
if err != nil {
|
_ = t.unsetRoute0(tunLink)
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, error) {
|
func (t *NativeTun) NewEndpoint() (stack.LinkEndpoint, error) {
|
||||||
var packetDispatchMode fdbased.PacketDispatchMode
|
|
||||||
if runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" {
|
|
||||||
packetDispatchMode = fdbased.PacketMMap
|
|
||||||
} else {
|
|
||||||
packetDispatchMode = fdbased.RecvMMsg
|
|
||||||
}
|
|
||||||
dupFdSize := int(math.Max(float64(runtime.NumCPU()/2), 1)) - 1
|
|
||||||
for i := 0; i < dupFdSize; i++ {
|
|
||||||
dupFd, err := syscall.Dup(t.fdList[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t.fdList = append(t.fdList, dupFd)
|
|
||||||
}
|
|
||||||
return fdbased.New(&fdbased.Options{
|
return fdbased.New(&fdbased.Options{
|
||||||
FDs: t.fdList,
|
FDs: []int{t.fd},
|
||||||
MTU: t.mtu,
|
MTU: t.mtu,
|
||||||
PacketDispatchMode: packetDispatchMode,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *NativeTun) Close() error {
|
func (t *NativeTun) Close() error {
|
||||||
|
var errors []error
|
||||||
|
if t.autoRoute {
|
||||||
|
errors = append(errors, t.unsetRoute())
|
||||||
|
}
|
||||||
|
errors = append(errors, unix.Close(t.fd))
|
||||||
|
return E.Errors(errors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tunTableIndex = 2022
|
||||||
|
|
||||||
|
func (t *NativeTun) routes(tunLink netlink.Link) []netlink.Route {
|
||||||
|
var routes []netlink.Route
|
||||||
|
if t.inet4Address.IsValid() {
|
||||||
|
routes = append(routes, netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: net.IPv4zero,
|
||||||
|
Mask: net.CIDRMask(0, 32),
|
||||||
|
},
|
||||||
|
LinkIndex: tunLink.Attrs().Index,
|
||||||
|
Table: tunTableIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if t.inet6Address.IsValid() {
|
||||||
|
routes = append(routes, netlink.Route{
|
||||||
|
Dst: &net.IPNet{
|
||||||
|
IP: net.IPv6zero,
|
||||||
|
Mask: net.CIDRMask(0, 128),
|
||||||
|
},
|
||||||
|
LinkIndex: tunLink.Attrs().Index,
|
||||||
|
Table: tunTableIndex,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return routes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NativeTun) rules() []*Rule {
|
||||||
|
var rules []*Rule
|
||||||
|
|
||||||
|
priority := 9000
|
||||||
|
|
||||||
|
it := NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.Invert = true
|
||||||
|
it.UIDRange = &RuleUIDRange{Start: 0, End: 0xFFFFFFFF - 1}
|
||||||
|
it.Goto = 9100
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
|
||||||
|
if t.inet4Address.IsValid() {
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.Dst = t.inet4Address.Masked()
|
||||||
|
it.Table = tunTableIndex
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.IPProtocol = unix.IPPROTO_ICMP
|
||||||
|
it.Goto = 9100
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.inet6Address.IsValid() {
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.Dst = t.inet6Address.Masked()
|
||||||
|
it.Table = tunTableIndex
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.IPProtocol = unix.IPPROTO_ICMPV6
|
||||||
|
it.Goto = 9100
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
}
|
||||||
|
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.Invert = true
|
||||||
|
it.DstPortRange = &RulePortRange{Start: 53, End: 53}
|
||||||
|
it.Table = unix.RT_TABLE_MAIN
|
||||||
|
it.SuppressPrefixLength = 0
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.Invert = true
|
||||||
|
it.IifName = "lo"
|
||||||
|
it.Table = tunTableIndex
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.IifName = "lo"
|
||||||
|
it.Src = netip.PrefixFrom(netip.IPv4Unspecified(), 32)
|
||||||
|
it.Table = tunTableIndex
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
|
||||||
|
if t.inet4Address.IsValid() {
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.IifName = "lo"
|
||||||
|
it.Src = t.inet4Address.Masked()
|
||||||
|
it.Table = tunTableIndex
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.inet6Address.IsValid() {
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = priority
|
||||||
|
it.IifName = "lo"
|
||||||
|
it.Src = t.inet6Address.Masked()
|
||||||
|
it.Table = tunTableIndex
|
||||||
|
rules = append(rules, it)
|
||||||
|
priority++
|
||||||
|
}
|
||||||
|
|
||||||
|
it = NewRule()
|
||||||
|
it.Priority = 9100
|
||||||
|
rules = append(rules, it)
|
||||||
|
|
||||||
|
return rules
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NativeTun) setRoute(tunLink netlink.Link) error {
|
||||||
|
for i, route := range t.routes(tunLink) {
|
||||||
|
err := netlink.RouteAdd(&route)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "add route ", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, rule := range t.rules() {
|
||||||
|
err := RuleAdd(rule)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "add rule ", i, "/", len(t.rules()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NativeTun) unsetRoute() error {
|
||||||
tunLink, err := netlink.LinkByName(t.name)
|
tunLink, err := netlink.LinkByName(t.name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if t.autoRoute {
|
return t.unsetRoute0(tunLink)
|
||||||
for _, route := range t.routes(tunLink) {
|
}
|
||||||
err = netlink.RouteDel(&route)
|
|
||||||
if err != nil {
|
func (t *NativeTun) unsetRoute0(tunLink netlink.Link) error {
|
||||||
return err
|
var errors []error
|
||||||
}
|
for _, route := range t.routes(tunLink) {
|
||||||
|
err := netlink.RouteDel(&route)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return E.Errors(common.Map(t.fdList, syscall.Close)...)
|
for _, rule := range t.rules() {
|
||||||
|
err := RuleDel(rule)
|
||||||
|
if err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return E.Errors(errors...)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue