Repository navigation
Expand file tree
/
Copy pathredirect_ingress_discard_android.go
More file actions
177 lines (166 loc) · 5.13 KB
/
Copy pathredirect_ingress_discard_android.go
File metadata and controls
177 lines (166 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package tun
import (
"encoding/binary"
"net/netip"
"os"
"runtime"
"unsafe"
"github.com/sagernet/netlink"
E "github.com/sagernet/sing/common/exceptions"
"golang.org/x/sys/unix"
)
// ConnectivityService.updateIngressToVpnAddressFiltering keys this map by the
// VPN address (IPv4-mapped) with the tun ifindex in both value slots, and the
// cgroup skb ingress program in netd.c drops packets for app uids to that
// address from any other interface, which includes the de-NATed replies of
// bypassed flows. ConnectivityService updates entries from LinkProperties diffs.
const androidIngressDiscardMapPath = "/sys/fs/bpf/netd_shared/map_netd_ingress_discard_map"
type bpfObjectAttribute struct {
pathname uint64
fileDescriptor uint32
fileFlags uint32
}
type bpfMapAttribute struct {
mapDescriptor uint32
_ uint32
key uint64
value uint64
flags uint64
}
type bpfObjectInfoAttribute struct {
fileDescriptor uint32
length uint32
info uint64
}
func bpfObjectGet(path string) (int, error) {
pathBytes, err := unix.BytePtrFromString(path)
if err != nil {
return 0, err
}
var pinner runtime.Pinner
pinner.Pin(pathBytes)
defer pinner.Unpin()
attribute := bpfObjectAttribute{pathname: uint64(uintptr(unsafe.Pointer(pathBytes)))}
descriptor, _, errno := unix.Syscall(unix.SYS_BPF, unix.BPF_OBJ_GET, uintptr(unsafe.Pointer(&attribute)), unsafe.Sizeof(attribute))
if errno != 0 {
return 0, errno
}
var info struct {
mapType uint32
id uint32
keySize uint32
valueSize uint32
}
pinner.Pin(&info)
infoAttribute := bpfObjectInfoAttribute{
fileDescriptor: uint32(descriptor),
length: uint32(unsafe.Sizeof(info)),
info: uint64(uintptr(unsafe.Pointer(&info))),
}
_, _, errno = unix.Syscall(unix.SYS_BPF, unix.BPF_OBJ_GET_INFO_BY_FD, uintptr(unsafe.Pointer(&infoAttribute)), unsafe.Sizeof(infoAttribute))
if errno != 0 {
unix.Close(int(descriptor))
return 0, errno
}
if info.mapType != unix.BPF_MAP_TYPE_HASH || info.keySize != 16 || info.valueSize != 8 {
unix.Close(int(descriptor))
return 0, E.New("unexpected ingress discard map layout: type ", info.mapType, ", key ", info.keySize, ", value ", info.valueSize)
}
return int(descriptor), nil
}
func bpfMapCall(command uintptr, mapDescriptor int, key []byte, value []byte, flags uint64) error {
var pinner runtime.Pinner
pinner.Pin(&key[0])
defer pinner.Unpin()
attribute := bpfMapAttribute{
mapDescriptor: uint32(mapDescriptor),
key: uint64(uintptr(unsafe.Pointer(&key[0]))),
flags: flags,
}
if value != nil {
pinner.Pin(&value[0])
attribute.value = uint64(uintptr(unsafe.Pointer(&value[0])))
}
_, _, errno := unix.Syscall(unix.SYS_BPF, command, uintptr(unsafe.Pointer(&attribute)), unsafe.Sizeof(attribute))
if errno != 0 {
return errno
}
return nil
}
func (r *autoRedirect) removeAndroidIngressDiscardRulesLocked() error {
if !r.enableIPv4 {
return nil
}
_, err := os.Stat(androidIngressDiscardMapPath)
if os.IsNotExist(err) {
return nil
}
if err != nil {
return E.Cause(err, "stat ingress discard map")
}
mapDescriptor, err := bpfObjectGet(androidIngressDiscardMapPath)
if err != nil {
if err == unix.ENOENT {
return nil
}
return E.Cause(err, "open ingress discard map")
}
defer unix.Close(mapDescriptor)
tunLink, err := netlink.LinkByName(r.tunOptions.Name)
if err != nil {
return E.Cause(err, "find tun interface")
}
for _, prefix := range r.tunOptions.Inet4Address {
address := prefix.Addr()
key := address.As16()
var value [8]byte
err = bpfMapCall(unix.BPF_MAP_LOOKUP_ELEM, mapDescriptor, key[:], value[:], 0)
if err == unix.ENOENT {
continue
}
if err != nil {
return E.Cause(err, "read ingress discard rule for ", address)
}
if int(binary.NativeEndian.Uint32(value[:4])) != tunLink.Attrs().Index ||
int(binary.NativeEndian.Uint32(value[4:])) != tunLink.Attrs().Index {
continue
}
err = bpfMapCall(unix.BPF_MAP_DELETE_ELEM, mapDescriptor, key[:], nil, 0)
if err != nil && err != unix.ENOENT {
return E.Cause(err, "remove ingress discard rule for ", address)
}
if r.androidIngressDiscardValues == nil {
r.androidIngressDiscardValues = make(map[netip.Addr][8]byte)
}
r.androidIngressDiscardValues[address] = value
r.logger.Debug("removed ingress discard rule for ", address)
}
return nil
}
func (r *autoRedirect) restoreAndroidIngressDiscardRulesLocked() {
values := r.androidIngressDiscardValues
r.androidIngressDiscardValues = nil
if len(values) == 0 {
return
}
tunLink, err := netlink.LinkByName(r.tunOptions.Name)
if err != nil {
return
}
mapDescriptor, err := bpfObjectGet(androidIngressDiscardMapPath)
if err != nil {
r.logger.Error("restore ingress discard rules: open map: ", err)
return
}
defer unix.Close(mapDescriptor)
for address, value := range values {
if int(binary.NativeEndian.Uint32(value[:4])) != tunLink.Attrs().Index {
continue
}
key := address.As16()
err = bpfMapCall(unix.BPF_MAP_UPDATE_ELEM, mapDescriptor, key[:], value[:], unix.BPF_NOEXIST)
if err != nil && err != unix.EEXIST {
r.logger.Error("restore ingress discard rule for ", address, ": ", err)
}
}
}