@@ -2,6 +2,7 @@ package nftables
2
2
3
3
import (
4
4
"bytes"
5
+ "encoding/binary"
5
6
"fmt"
6
7
"net/netip"
7
8
"os/exec"
@@ -20,7 +21,7 @@ import (
20
21
21
22
var ifaceMock = & iFaceMock {
22
23
NameFunc : func () string {
23
- return "lo "
24
+ return "wg-test "
24
25
},
25
26
AddressFunc : func () wgaddr.Address {
26
27
return wgaddr.Address {
@@ -103,9 +104,8 @@ func TestNftablesManager(t *testing.T) {
103
104
Kind : expr .VerdictAccept ,
104
105
},
105
106
}
106
- compareExprsIgnoringCounters (t , rules [0 ].Exprs , expectedExprs1 )
107
-
108
- expectedExprs2 := []expr.Any {
107
+ // Since DROP rules are inserted at position 0, the DROP rule comes first
108
+ expectedDropExprs := []expr.Any {
109
109
& expr.Payload {
110
110
DestRegister : 1 ,
111
111
Base : expr .PayloadBaseNetworkHeader ,
@@ -141,7 +141,12 @@ func TestNftablesManager(t *testing.T) {
141
141
},
142
142
& expr.Verdict {Kind : expr .VerdictDrop },
143
143
}
144
- require .ElementsMatch (t , rules [1 ].Exprs , expectedExprs2 , "expected the same expressions" )
144
+
145
+ // Compare DROP rule at position 0 (inserted first due to InsertRule)
146
+ compareExprsIgnoringCounters (t , rules [0 ].Exprs , expectedDropExprs )
147
+
148
+ // Compare connection tracking rule at position 1 (pushed down by DROP rule insertion)
149
+ compareExprsIgnoringCounters (t , rules [1 ].Exprs , expectedExprs1 )
145
150
146
151
for _ , r := range rule {
147
152
err = manager .DeletePeerRule (r )
@@ -160,10 +165,90 @@ func TestNftablesManager(t *testing.T) {
160
165
require .NoError (t , err , "failed to reset" )
161
166
}
162
167
168
+ func TestNftablesManagerRuleOrder (t * testing.T ) {
169
+ // This test verifies rule insertion order in nftables peer ACLs
170
+ // We add accept rule first, then deny rule to test ordering behavior
171
+ manager , err := Create (ifaceMock )
172
+ require .NoError (t , err )
173
+ require .NoError (t , manager .Init (nil ))
174
+
175
+ defer func () {
176
+ err = manager .Close (nil )
177
+ require .NoError (t , err )
178
+ }()
179
+
180
+ ip := netip .MustParseAddr ("100.96.0.2" ).Unmap ()
181
+ testClient := & nftables.Conn {}
182
+
183
+ // Add accept rule first
184
+ _ , err = manager .AddPeerFiltering (nil , ip .AsSlice (), fw .ProtocolTCP , nil , & fw.Port {Values : []uint16 {80 }}, fw .ActionAccept , "accept-http" )
185
+ require .NoError (t , err , "failed to add accept rule" )
186
+
187
+ // Add deny rule second for the same traffic
188
+ _ , err = manager .AddPeerFiltering (nil , ip .AsSlice (), fw .ProtocolTCP , nil , & fw.Port {Values : []uint16 {80 }}, fw .ActionDrop , "deny-http" )
189
+ require .NoError (t , err , "failed to add deny rule" )
190
+
191
+ err = manager .Flush ()
192
+ require .NoError (t , err , "failed to flush" )
193
+
194
+ rules , err := testClient .GetRules (manager .aclManager .workTable , manager .aclManager .chainInputRules )
195
+ require .NoError (t , err , "failed to get rules" )
196
+
197
+ t .Logf ("Found %d rules in nftables chain" , len (rules ))
198
+
199
+ // Find the accept and deny rules and verify deny comes before accept
200
+ var acceptRuleIndex , denyRuleIndex int = - 1 , - 1
201
+ for i , rule := range rules {
202
+ hasAcceptHTTPSet := false
203
+ hasDenyHTTPSet := false
204
+ hasPort80 := false
205
+ var action string
206
+
207
+ for _ , e := range rule .Exprs {
208
+ // Check for set lookup
209
+ if lookup , ok := e .(* expr.Lookup ); ok {
210
+ if lookup .SetName == "accept-http" {
211
+ hasAcceptHTTPSet = true
212
+ } else if lookup .SetName == "deny-http" {
213
+ hasDenyHTTPSet = true
214
+ }
215
+ }
216
+ // Check for port 80
217
+ if cmp , ok := e .(* expr.Cmp ); ok {
218
+ if cmp .Op == expr .CmpOpEq && len (cmp .Data ) == 2 && binary .BigEndian .Uint16 (cmp .Data ) == 80 {
219
+ hasPort80 = true
220
+ }
221
+ }
222
+ // Check for verdict
223
+ if verdict , ok := e .(* expr.Verdict ); ok {
224
+ if verdict .Kind == expr .VerdictAccept {
225
+ action = "ACCEPT"
226
+ } else if verdict .Kind == expr .VerdictDrop {
227
+ action = "DROP"
228
+ }
229
+ }
230
+ }
231
+
232
+ if hasAcceptHTTPSet && hasPort80 && action == "ACCEPT" {
233
+ t .Logf ("Rule [%d]: accept-http set + Port 80 + ACCEPT" , i )
234
+ acceptRuleIndex = i
235
+ } else if hasDenyHTTPSet && hasPort80 && action == "DROP" {
236
+ t .Logf ("Rule [%d]: deny-http set + Port 80 + DROP" , i )
237
+ denyRuleIndex = i
238
+ }
239
+ }
240
+
241
+ require .NotEqual (t , - 1 , acceptRuleIndex , "accept rule should exist in nftables" )
242
+ require .NotEqual (t , - 1 , denyRuleIndex , "deny rule should exist in nftables" )
243
+ require .Less (t , denyRuleIndex , acceptRuleIndex ,
244
+ "deny rule should come before accept rule in nftables chain (deny at index %d, accept at index %d)" ,
245
+ denyRuleIndex , acceptRuleIndex )
246
+ }
247
+
163
248
func TestNFtablesCreatePerformance (t * testing.T ) {
164
249
mock := & iFaceMock {
165
250
NameFunc : func () string {
166
- return "lo "
251
+ return "wg-test "
167
252
},
168
253
AddressFunc : func () wgaddr.Address {
169
254
return wgaddr.Address {
0 commit comments