-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
129 lines (107 loc) · 3.72 KB
/
main.go
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
package main
import (
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/rlimit"
"github.com/cockroachdb/errors"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
"log"
"regexp"
)
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go bpf bpf.c
var blockedIfacesName = regexp.MustCompile(`^cilium_|^lxc`)
func main() {
// Allow the current process to lock memory for eBPF resources.
if err := rlimit.RemoveMemlock(); err != nil {
panic(errors.Wrap(err, "remove memory limit for the current process"))
}
tsCgroupPath, err := tailscaleCgroup()
if err != nil {
panic(errors.Wrap(err, "detect tailscaled cgroup path"))
}
log.Printf("found cgroup for tailscale: %s\n", tsCgroupPath)
// Load pre-compiled programs and maps into the kernel.
objs := bpfObjects{}
if err := loadBpfObjects(&objs, nil); err != nil {
panic(errors.Wrap(err, "load eBPF objects"))
}
defer objs.Close()
log.Println("loaded eBPF programs and maps into the kernel")
// Link eBPF programs to the cgroup.
lEgress, err := link.AttachCgroup(link.CgroupOptions{
Path: tsCgroupPath,
Attach: ebpf.AttachCGroupInetEgress,
Program: objs.RestrictNetworkInterfacesEgress,
})
if err != nil {
panic(errors.Wrap(err, "link restrict_network_interfaces_egress to the cgroup"))
}
defer lEgress.Close()
lIngress, err := link.AttachCgroup(link.CgroupOptions{
Path: tsCgroupPath,
Attach: ebpf.AttachCGroupInetIngress,
Program: objs.RestrictNetworkInterfacesIngress,
})
if err != nil {
panic(errors.Wrap(err, "link restrict_network_interfaces_ingress to the cgroup"))
}
defer lIngress.Close()
log.Println("attached eBPF programs to the cgroup")
ch := make(chan netlink.LinkUpdate)
done := make(chan struct{})
handleError := func(err error) {
panic(errors.Wrap(err, "process a netlink message"))
}
if err := netlink.LinkSubscribeWithOptions(ch, done, netlink.LinkSubscribeOptions{
ErrorCallback: handleError,
ListExisting: true,
}); err != nil {
panic(errors.Wrap(err, "subscribe to link changes"))
}
log.Println("subscribed to link changes")
for u := range ch {
if err := handleLinkUpdate(objs.IfacesMap, &u); err != nil {
panic(errors.Wrap(err, "process LinkUpdate"))
}
}
}
func handleLinkUpdate(ifacesMap *ebpf.Map, u *netlink.LinkUpdate) error {
ifaceName := u.Link.Attrs().Name
ifaceIdx := uint32(u.Index)
switch u.Header.Type {
case unix.RTM_NEWLINK:
log.Printf("interface created or updated: %d (%s)\n", ifaceIdx, ifaceName)
if blockedIfacesName.MatchString(ifaceName) {
if err := blockInterface(ifacesMap, ifaceIdx); err != nil {
return errors.Wrapf(err, "block interface %d (%s)", ifaceIdx, ifaceName)
}
} else {
if err := unblockInterface(ifacesMap, ifaceIdx); err != nil {
return errors.Wrapf(err, "unblock interface %d (%s)", ifaceIdx, ifaceName)
}
}
case unix.RTM_DELLINK:
log.Printf("interface removed: %d (%s)\n", ifaceIdx, ifaceName)
if err := unblockInterface(ifacesMap, ifaceIdx); err != nil {
return errors.Wrapf(err, "unblock interface %d (%s)", ifaceIdx, ifaceName)
}
default:
return errors.Newf("received a netlink message of unknown type %x for interface %d (%s)", u.Header.Type, ifaceIdx, ifaceName)
}
return nil
}
func blockInterface(ifacesMap *ebpf.Map, ifaceIdx uint32) error {
log.Printf("blocking interface: %d\n", ifaceIdx)
if err := ifacesMap.Put(ifaceIdx, uint8(0)); err != nil {
return errors.Wrap(err, "add an entry to ifacesMap")
}
return nil
}
func unblockInterface(ifacesMap *ebpf.Map, ifaceIdx uint32) error {
log.Printf("unblocking interface: %d\n", ifaceIdx)
if err := ifacesMap.Delete(ifaceIdx); err != nil && !errors.Is(err, ebpf.ErrKeyNotExist) {
return errors.Wrap(err, "remove an entry from ifacesMap")
}
return nil
}