Skip to content

Commit e31f365

Browse files
authored
Merge pull request #282 from AkihiroSuda/guestagent-watch-audit
guestagent: watch NETFILTER_CFG audit events to reduce polling
2 parents 1b260c2 + cee3ff8 commit e31f365

File tree

5 files changed

+93
-7
lines changed

5 files changed

+93
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ The current default spec:
182182

183183
- Hypervisor: QEMU with HVF accelerator
184184
- Filesystem sharing: [reverse sshfs](https://github.com/lima-vm/sshocker/blob/v0.2.0/pkg/reversesshfs/reversesshfs.go) (planned to be replaced with 9p soon)
185-
- Port forwarding: `ssh -L`, automated by watching `/proc/net/tcp` in the guest
185+
- Port forwarding: `ssh -L`, automated by watching `/proc/net/tcp` and `iptables` events in the guest
186186

187187
## Developer guide
188188

cmd/lima-guestagent/daemon_linux.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ func daemonAction(cmd *cobra.Command, args []string) error {
4646
return ticker.C, ticker.Stop
4747
}
4848

49-
agent := guestagent.New(newTicker)
49+
agent, err := guestagent.New(newTicker, tick*20)
50+
if err != nil {
51+
return err
52+
}
5053
backend := &server.Backend{
5154
Agent: agent,
5255
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/digitalocean/go-qemu v0.0.0-20210326154740-ac9e0b687001
1212
github.com/diskfs/go-diskfs v1.2.0
1313
github.com/docker/go-units v0.4.0
14+
github.com/elastic/go-libaudit/v2 v2.2.0
1415
github.com/gorilla/mux v1.8.0
1516
github.com/hashicorp/go-multierror v1.1.1
1617
github.com/lima-vm/sshocker v0.2.2

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
300300
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
301301
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
302302
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
303+
github.com/elastic/go-libaudit/v2 v2.2.0 h1:TY3FDpG4Zr9Qnv6KYW6olYr/U+nfu0rD2QAbv75VxMQ=
304+
github.com/elastic/go-libaudit/v2 v2.2.0/go.mod h1:MM/l/4xV7ilcl+cIblL8Zn448J7RZaDwgNLE4gNKYPg=
303305
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
304306
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
305307
github.com/elazarl/goproxy v0.0.0-20210801061803-8e322dfb79c4/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
@@ -666,6 +668,7 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
666668
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
667669
github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
668670
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
671+
github.com/pkg/errors v0.8.1-0.20170505043639-c605e284fe17/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
669672
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
670673
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
671674
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -768,6 +771,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
768771
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
769772
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
770773
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
774+
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
771775
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
772776
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
773777
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=

pkg/guestagent/guestagent_linux.go

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,88 @@ import (
55
"encoding/binary"
66
"errors"
77
"reflect"
8+
"sync"
89
"time"
910

11+
"github.com/elastic/go-libaudit/v2"
12+
"github.com/elastic/go-libaudit/v2/auparse"
1013
"github.com/lima-vm/lima/pkg/guestagent/api"
1114
"github.com/lima-vm/lima/pkg/guestagent/iptables"
1215
"github.com/lima-vm/lima/pkg/guestagent/procnettcp"
1316
"github.com/sirupsen/logrus"
1417
"github.com/yalue/native_endian"
1518
)
1619

17-
func New(newTicker func() (<-chan time.Time, func())) Agent {
18-
return &agent{
20+
func New(newTicker func() (<-chan time.Time, func()), iptablesIdle time.Duration) (Agent, error) {
21+
a := &agent{
1922
newTicker: newTicker,
2023
}
24+
25+
auditClient, err := libaudit.NewMulticastAuditClient(nil)
26+
if err != nil {
27+
return nil, err
28+
}
29+
auditStatus, err := auditClient.GetStatus()
30+
if err != nil {
31+
return nil, err
32+
}
33+
if auditStatus.Enabled == 0 {
34+
if err = auditClient.SetEnabled(true, libaudit.WaitForReply); err != nil {
35+
return nil, err
36+
}
37+
}
38+
39+
go a.setWorthCheckingIPTablesRoutine(auditClient, iptablesIdle)
40+
return a, nil
2141
}
2242

2343
type agent struct {
2444
// Ticker is like time.Ticker.
2545
// We can't use inotify for /proc/net/tcp, so we need this ticker to
2646
// reload /proc/net/tcp.
2747
newTicker func() (<-chan time.Time, func())
48+
49+
worthCheckingIPTables bool
50+
worthCheckingIPTablesMu sync.RWMutex
51+
latestIPTables []iptables.Entry
52+
latestIPTablesMu sync.RWMutex
53+
}
54+
55+
// setWorthCheckingIPTablesRoutine sets worthCheckingIPTables to be true
56+
// when received NETFILTER_CFG audit message.
57+
//
58+
// setWorthCheckingIPTablesRoutine sets worthCheckingIPTables to be false
59+
// when no NETFILTER_CFG audit message was received for the iptablesIdle time.
60+
func (a *agent) setWorthCheckingIPTablesRoutine(auditClient *libaudit.AuditClient, iptablesIdle time.Duration) {
61+
var latestTrue time.Time
62+
go func() {
63+
for {
64+
time.Sleep(iptablesIdle)
65+
a.worthCheckingIPTablesMu.Lock()
66+
// time is monotonic, see https://pkg.go.dev/time#hdr-Monotonic_Clocks
67+
elapsedSinceLastTrue := time.Since(latestTrue)
68+
if elapsedSinceLastTrue >= iptablesIdle {
69+
logrus.Debug("setWorthCheckingIPTablesRoutine(): setting to false")
70+
a.worthCheckingIPTables = false
71+
}
72+
a.worthCheckingIPTablesMu.Unlock()
73+
}
74+
}()
75+
for {
76+
msg, err := auditClient.Receive(false)
77+
if err != nil {
78+
logrus.Error(err)
79+
continue
80+
}
81+
switch msg.Type {
82+
case auparse.AUDIT_NETFILTER_CFG:
83+
a.worthCheckingIPTablesMu.Lock()
84+
logrus.Debug("setWorthCheckingIPTablesRoutine(): setting to true")
85+
a.worthCheckingIPTables = true
86+
latestTrue = time.Now()
87+
a.worthCheckingIPTablesMu.Unlock()
88+
}
89+
}
2890
}
2991

3092
type eventState struct {
@@ -131,10 +193,26 @@ func (a *agent) LocalPorts(ctx context.Context) ([]api.IPPort, error) {
131193
}
132194
}
133195

134-
ipts, err := iptables.GetPorts()
135-
if err != nil {
136-
return res, err
196+
a.worthCheckingIPTablesMu.RLock()
197+
worthCheckingIPTables := a.worthCheckingIPTables
198+
a.worthCheckingIPTablesMu.RUnlock()
199+
logrus.Debugf("LocalPorts(): worthCheckingIPTables=%v", worthCheckingIPTables)
200+
201+
var ipts []iptables.Entry
202+
if a.worthCheckingIPTables {
203+
ipts, err = iptables.GetPorts()
204+
if err != nil {
205+
return res, err
206+
}
207+
a.latestIPTablesMu.Lock()
208+
a.latestIPTables = ipts
209+
a.latestIPTablesMu.Unlock()
210+
} else {
211+
a.latestIPTablesMu.RLock()
212+
ipts = a.latestIPTables
213+
a.latestIPTablesMu.RUnlock()
137214
}
215+
138216
for _, ipt := range ipts {
139217
// Make sure the port isn't already listed from procnettcp
140218
found := false

0 commit comments

Comments
 (0)