Skip to content

Commit 35f95ac

Browse files
authored
Add DestroyRule method (#341)
DestroyRule removes the specified rule and does not return an error during Flush if the rule does not exist. This change also refactors DelRule into an internal delRule helper shared by both DelRule and DestroyRule.
1 parent 6c7b7cb commit 35f95ac

File tree

2 files changed

+91
-7
lines changed

2 files changed

+91
-7
lines changed

nftables_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,6 +1245,69 @@ func TestDelRule(t *testing.T) {
12451245
t.Fatal(err)
12461246
}
12471247
}
1248+
func TestDestroyRule(t *testing.T) {
1249+
var tests = []struct {
1250+
name string
1251+
createRule bool
1252+
}{
1253+
{
1254+
name: "non_existent",
1255+
createRule: false,
1256+
},
1257+
{
1258+
name: "existent",
1259+
createRule: true,
1260+
},
1261+
}
1262+
1263+
for _, tt := range tests {
1264+
t.Run(tt.name, func(t *testing.T) {
1265+
conn, newNS := nftest.OpenSystemConn(t, *enableSysTests)
1266+
defer nftest.CleanupSystemConn(t, newNS)
1267+
defer conn.FlushRuleset()
1268+
1269+
table := &nftables.Table{
1270+
Name: "test-table",
1271+
Family: nftables.TableFamilyIPv4,
1272+
}
1273+
conn.AddTable(table)
1274+
chain := &nftables.Chain{
1275+
Name: "test-chain",
1276+
Table: table,
1277+
}
1278+
conn.AddChain(chain)
1279+
rule := &nftables.Rule{
1280+
Table: table,
1281+
Chain: chain,
1282+
Exprs: []expr.Any{
1283+
&expr.Verdict{Kind: expr.VerdictAccept},
1284+
},
1285+
}
1286+
if tt.createRule {
1287+
conn.AddRule(rule)
1288+
} else {
1289+
// Assign a handle that does not exist
1290+
rule.Handle = 1234
1291+
}
1292+
if err := conn.Flush(); err != nil {
1293+
t.Fatalf("conn.Flush() failed: %v", err)
1294+
}
1295+
1296+
conn.DestroyRule(rule)
1297+
if err := conn.Flush(); err != nil {
1298+
t.Fatalf("conn.Flush() failed: %v", err)
1299+
}
1300+
1301+
rules, err := conn.GetRules(table, chain)
1302+
if err != nil {
1303+
t.Fatalf("conn.GetRules() failed: %v", err)
1304+
}
1305+
if len(rules) != 0 {
1306+
t.Errorf("expected 0 rules after destroying rule, got %d", len(rules))
1307+
}
1308+
})
1309+
}
1310+
}
12481311

12491312
func TestLog(t *testing.T) {
12501313
want := [][]byte{

rule.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727

2828
var (
2929
newRuleHeaderType = nftMsgNewRule.HeaderType()
30-
delRuleHeaderType = nftMsgDelRule.HeaderType()
3130
)
3231

3332
// This constant is missing at unix.NFTA_RULE_POSITION_ID.
@@ -321,9 +320,9 @@ func (cc *Conn) InsertRule(r *Rule) *Rule {
321320
return cc.newRule(r, operationInsert)
322321
}
323322

324-
// DelRule deletes the specified Rule. Either the Handle or ID of the
325-
// rule must be set.
326-
func (cc *Conn) DelRule(r *Rule) error {
323+
// delRule deletes the specified Rule. If the destroy flag is set, then the
324+
// message type used is NFT_MSG_DESTROYRULE instead of NFT_MSG_DELRULE.
325+
func (cc *Conn) delRule(r *Rule, destroy bool) error {
327326
cc.mu.Lock()
328327
defer cc.mu.Unlock()
329328
data := cc.marshalAttr([]netlink.Attribute{
@@ -345,9 +344,14 @@ func (cc *Conn) DelRule(r *Rule) error {
345344
}
346345
flags := netlink.Request
347346

347+
msgType := nftMsgDelRule
348+
if destroy {
349+
msgType = nftMsgDestroyRule
350+
}
351+
348352
cc.messages = append(cc.messages, netlinkMessage{
349353
Header: netlink.Header{
350-
Type: delRuleHeaderType,
354+
Type: msgType.HeaderType(),
351355
Flags: flags,
352356
},
353357
Data: append(extraHeader(uint8(r.Table.Family), 0), data...),
@@ -356,9 +360,26 @@ func (cc *Conn) DelRule(r *Rule) error {
356360
return nil
357361
}
358362

363+
// DelRule deletes the specified Rule. Either the Handle or ID of the
364+
// rule must be set.
365+
func (cc *Conn) DelRule(r *Rule) error {
366+
return cc.delRule(r, false)
367+
}
368+
369+
// DestroyRule deletes the specified rule but unlike DelRule, it will not
370+
// return an error upon Flush if the rule does not exist. Either the Handle
371+
// or ID of the rule must be set.
372+
// Requires a kernel version >= 6.3.
373+
func (cc *Conn) DestroyRule(r *Rule) error {
374+
return cc.delRule(r, true)
375+
}
376+
359377
func ruleFromMsg(fam TableFamily, msg netlink.Message) (*Rule, error) {
360-
if got, want1, want2 := msg.Header.Type, newRuleHeaderType, delRuleHeaderType; got != want1 && got != want2 {
361-
return nil, fmt.Errorf("unexpected header type: got %v, want %v or %v", msg.Header.Type, want1, want2)
378+
switch msg.Header.Type {
379+
case nftMsgNewRule.HeaderType(), nftMsgDelRule.HeaderType(), nftMsgDestroyRule.HeaderType():
380+
// Valid message type, continue processing
381+
default:
382+
return nil, fmt.Errorf("unexpected header type: %v", msg.Header.Type)
362383
}
363384
ad, err := netlink.NewAttributeDecoder(msg.Data[4:])
364385
if err != nil {

0 commit comments

Comments
 (0)