Skip to content
This repository was archived by the owner on Oct 14, 2025. It is now read-only.

Commit 4008843

Browse files
committed
fastpath: more resilient and remove userspace dependencies
Only offload connection that are established and that belongs to the devices that are being offloaded. Remove the dependency on userspace and use the netlink interface for communicating with nftables.
1 parent a5d8254 commit 4008843

File tree

3 files changed

+130
-56
lines changed

3 files changed

+130
-56
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.23.0
55
require (
66
github.com/aws/aws-sdk-go v1.55.5
77
github.com/containerd/nri v0.9.0
8+
github.com/google/nftables v0.2.1-0.20241219092456-e99829fb4f26
89
github.com/prometheus/client_golang v1.20.5
910
github.com/vishvananda/netlink v1.3.0
1011
k8s.io/api v0.32.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
5656
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
5757
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
5858
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
59+
github.com/google/nftables v0.2.1-0.20241219092456-e99829fb4f26 h1:UHjgssveCChQTtQv4v34YS4czWH9ubY7goTwoeUR5FQ=
60+
github.com/google/nftables v0.2.1-0.20241219092456-e99829fb4f26/go.mod h1:Fo/xFnOxWlRQtnHdNi46KbIjufTDzbKhtghpWrmsSUg=
5961
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
6062
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
6163
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=

pkg/fastpath/fastpath.go

Lines changed: 127 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,75 +4,41 @@ package fastpath
44

55
import (
66
"context"
7+
"fmt"
8+
"math"
79
"net"
810
"time"
911

12+
"github.com/google/nftables"
13+
"github.com/google/nftables/binaryutil"
14+
"github.com/google/nftables/expr"
1015
"github.com/vishvananda/netlink"
1116
"golang.org/x/time/rate"
1217

1318
"k8s.io/apimachinery/pkg/util/sets"
1419
"k8s.io/klog/v2"
15-
"sigs.k8s.io/knftables"
1620
)
1721

1822
const (
19-
kindnetFlowtable = "kindnet-flowtables"
20-
fastPathChain = "kindnet-fastpath-chain"
23+
kindnetFlowtable = "kindnet-flowtables"
24+
kindnetSetDevices = "kindnet-set-devices"
25+
fastPathChain = "kindnet-fastpath-chain"
2126
)
2227

2328
func NewFastpathAgent(packetThresold int) (*FastPathAgent, error) {
24-
klog.V(2).Info("Initializing nftables")
25-
nft, err := knftables.New(knftables.InetFamily, "kindnet-fastpath")
26-
if err != nil {
27-
return nil, err
29+
if packetThresold > math.MaxUint32 {
30+
packetThresold = math.MaxUint32
2831
}
2932
return &FastPathAgent{
30-
nft: nft,
31-
packetThresold: packetThresold,
33+
packetThresold: uint32(packetThresold),
3234
}, nil
3335
}
3436

3537
type FastPathAgent struct {
36-
nft knftables.Interface
37-
packetThresold int
38+
packetThresold uint32
3839
}
3940

4041
func (ma *FastPathAgent) Run(ctx context.Context) error {
41-
klog.Info("Syncing nftables rules")
42-
table := &knftables.Table{
43-
Comment: knftables.PtrTo("rules for kindnet fastpath"),
44-
}
45-
tx := ma.nft.NewTransaction()
46-
// do it once to delete the existing table
47-
tx.Add(table)
48-
tx.Delete(table)
49-
tx.Add(table)
50-
51-
tx.Add(&knftables.Flowtable{
52-
Name: kindnetFlowtable,
53-
})
54-
55-
tx.Add(&knftables.Chain{
56-
Name: fastPathChain,
57-
Type: knftables.PtrTo(knftables.FilterType),
58-
Hook: knftables.PtrTo(knftables.ForwardHook),
59-
Priority: knftables.PtrTo(knftables.DNATPriority + "-10"),
60-
})
61-
62-
tx.Add(&knftables.Rule{
63-
Chain: fastPathChain,
64-
Rule: knftables.Concat(
65-
"ct packets >", ma.packetThresold,
66-
"flow offload", "@", kindnetFlowtable,
67-
"counter",
68-
),
69-
})
70-
71-
err := ma.nft.Run(ctx, tx)
72-
if err != nil {
73-
klog.Error(err, "failed to add network interfaces to the flowtable")
74-
}
75-
7642
minInterval := 5 * time.Second
7743
maxInterval := 1 * time.Minute
7844
rateLimiter := rate.NewLimiter(rate.Every(minInterval), 1)
@@ -97,12 +63,7 @@ func (ma *FastPathAgent) Run(ctx context.Context) error {
9763
}
9864

9965
if len(ifnames) > 0 && !ifnames.Equal(currentIf) {
100-
tx := ma.nft.NewTransaction()
101-
tx.Add(&knftables.Flowtable{
102-
Name: kindnetFlowtable,
103-
Devices: ifnames.UnsortedList(),
104-
})
105-
err := ma.nft.Run(ctx, tx)
66+
err := ma.syncRules(ifnames.UnsortedList())
10667
if err != nil {
10768
klog.Error(err, "failed to add network interfaces to the flowtable")
10869
} else {
@@ -149,14 +110,124 @@ func (ma *FastPathAgent) getNodeInterfaces() (sets.Set[string], error) {
149110
return ifNames, nil
150111
}
151112

113+
func (ma *FastPathAgent) syncRules(devices []string) error {
114+
klog.V(2).Info("Syncing kindnet-fastpath nftables rules")
115+
nft, err := nftables.New()
116+
if err != nil {
117+
return fmt.Errorf("fastpath failure, can not start nftables:%v", err)
118+
}
119+
120+
// add + delete + add for flushing all the table
121+
fastpathTable := &nftables.Table{
122+
Name: "kindnet-fastpath",
123+
Family: nftables.TableFamilyINet,
124+
}
125+
nft.AddTable(fastpathTable)
126+
nft.DelTable(fastpathTable)
127+
nft.AddTable(fastpathTable)
128+
129+
devicesSet := &nftables.Set{
130+
Table: fastpathTable,
131+
Name: kindnetSetDevices,
132+
KeyType: nftables.TypeIFName,
133+
KeyByteOrder: binaryutil.NativeEndian,
134+
}
135+
136+
elements := []nftables.SetElement{}
137+
for _, dev := range devices {
138+
elements = append(elements, nftables.SetElement{
139+
Key: ifname(dev),
140+
})
141+
}
142+
143+
if err := nft.AddSet(devicesSet, elements); err != nil {
144+
return fmt.Errorf("failed to add Set %s : %v", devicesSet.Name, err)
145+
}
146+
147+
flowtable := &nftables.Flowtable{
148+
Table: fastpathTable,
149+
Name: kindnetFlowtable,
150+
Devices: devices,
151+
Hooknum: nftables.FlowtableHookIngress,
152+
Priority: nftables.FlowtablePriorityRef(5),
153+
}
154+
nft.AddFlowtable(flowtable)
155+
156+
chain := nft.AddChain(&nftables.Chain{
157+
Name: fastPathChain,
158+
Table: fastpathTable,
159+
Type: nftables.ChainTypeFilter,
160+
Hooknum: nftables.ChainHookForward,
161+
Priority: nftables.ChainPriorityMangle, // before DNAT
162+
})
163+
164+
// only offload devices that are being tracked
165+
// TODO: check if this is really needed, we are using a set in addition
166+
// to the flowtable.
167+
nft.AddRule(&nftables.Rule{
168+
Table: fastpathTable,
169+
Chain: chain,
170+
Exprs: []expr.Any{
171+
&expr.Meta{Key: expr.MetaKeyIIFNAME, SourceRegister: false, Register: 0x1},
172+
&expr.Lookup{SourceRegister: 0x1, DestRegister: 0x0, IsDestRegSet: false, SetName: kindnetSetDevices, Invert: true},
173+
&expr.Verdict{Kind: expr.VerdictReturn},
174+
},
175+
})
176+
177+
nft.AddRule(&nftables.Rule{
178+
Table: fastpathTable,
179+
Chain: chain,
180+
Exprs: []expr.Any{
181+
&expr.Meta{Key: expr.MetaKeyOIFNAME, SourceRegister: false, Register: 0x1},
182+
&expr.Lookup{SourceRegister: 0x1, DestRegister: 0x0, IsDestRegSet: false, SetName: kindnetSetDevices, Invert: true},
183+
&expr.Verdict{Kind: expr.VerdictReturn},
184+
},
185+
})
186+
187+
// ct packets > packetThresold flow add @kindnet-flowtables counter
188+
nft.AddRule(&nftables.Rule{
189+
Table: fastpathTable,
190+
Chain: chain,
191+
Exprs: []expr.Any{
192+
&expr.Ct{Register: 0x1, SourceRegister: false, Key: expr.CtKeySTATE, Direction: 0x0},
193+
&expr.Bitwise{SourceRegister: 0x1, DestRegister: 0x1, Len: 0x4, Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitESTABLISHED), Xor: binaryutil.NativeEndian.PutUint32(0)},
194+
&expr.Cmp{Op: 0x1, Register: 0x1, Data: []uint8{0x0, 0x0, 0x0, 0x0}},
195+
&expr.Ct{Register: 0x1, SourceRegister: false, Key: expr.CtKeyPKTS, Direction: 0x0},
196+
&expr.Cmp{Op: expr.CmpOpGt, Register: 0x1, Data: binaryutil.NativeEndian.PutUint64(uint64(ma.packetThresold))},
197+
&expr.FlowOffload{Name: kindnetFlowtable},
198+
&expr.Counter{},
199+
},
200+
})
201+
202+
err = nft.Flush()
203+
if err != nil {
204+
return fmt.Errorf("failed to create kindnet-fastpath table: %v", err)
205+
}
206+
return nil
207+
}
208+
152209
func (ma *FastPathAgent) CleanRules() {
153-
tx := ma.nft.NewTransaction()
210+
nft, err := nftables.New()
211+
if err != nil {
212+
klog.Infof("fastpath cleanup failure, can not start nftables:%v", err)
213+
return
214+
}
154215
// Add+Delete is idempotent and won't return an error if the table doesn't already
155216
// exist.
156-
tx.Add(&knftables.Table{})
157-
tx.Delete(&knftables.Table{})
217+
fastpathTable := nft.AddTable(&nftables.Table{
218+
Family: nftables.TableFamilyINet,
219+
Name: "kindnet-fastpath",
220+
})
221+
nft.DelTable(fastpathTable)
158222

159-
if err := ma.nft.Run(context.TODO(), tx); err != nil {
223+
err = nft.Flush()
224+
if err != nil {
160225
klog.Infof("error deleting nftables rules %v", err)
161226
}
162227
}
228+
229+
func ifname(n string) []byte {
230+
b := make([]byte, 16)
231+
copy(b, []byte(n+"\x00"))
232+
return b
233+
}

0 commit comments

Comments
 (0)