From 9871a53d2d4834982a735c1b41cade5c9adf5292 Mon Sep 17 00:00:00 2001 From: Chen Tang Date: Wed, 25 Mar 2026 11:25:49 +0800 Subject: [PATCH] ipset: fix bitmap:port entry add failing with invalid protocol buildEntryData() unconditionally sends IPSET_ATTR_PROTO (defaulting to TCP) whenever Port is set. For bitmap:port ipsets, this protocol attribute is not expected and newer kernels reject it with "invalid protocol". Decouple Protocol and Port attributes: only send IPSET_ATTR_PROTO when entry.Protocol is explicitly set. This matches the behavior of the ipset CLI tool, which does not send protocol for bitmap:port. Note: callers using hash:ip,port or hash:net,port,net must now explicitly set entry.Protocol, as the implicit TCP default is removed. Also add a bitmap:port test case to TestIpsetCreateListAddDelDestroy and fix the port/protocol assertion to handle nil Protocol. Fixes #1054 --- ipset_linux.go | 10 ++++------ ipset_linux_test.go | 24 +++++++++++++++++++++--- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/ipset_linux.go b/ipset_linux.go index f4c05229f..95921a4de 100644 --- a/ipset_linux.go +++ b/ipset_linux.go @@ -314,13 +314,11 @@ func buildEntryData(entry *IPSetEntry) (*nl.RtAttr, error) { data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_CIDR2, nl.Uint8Attr(entry.CIDR2))) } - if entry.Port != nil { - if entry.Protocol == nil { - // use tcp protocol as default - val := uint8(unix.IPPROTO_TCP) - entry.Protocol = &val - } + if entry.Protocol != nil { data.AddChild(nl.NewRtAttr(nl.IPSET_ATTR_PROTO, nl.Uint8Attr(*entry.Protocol))) + } + + if entry.Port != nil { buf := make([]byte, 2) binary.BigEndian.PutUint16(buf, *entry.Port) data.AddChild(nl.NewRtAttr(int(nl.IPSET_ATTR_PORT|nl.NLA_F_NET_BYTEORDER), buf)) diff --git a/ipset_linux_test.go b/ipset_linux_test.go index 298c3a3a9..dc3c5c990 100644 --- a/ipset_linux_test.go +++ b/ipset_linux_test.go @@ -440,6 +440,21 @@ func TestIpsetCreateListAddDelDestroyWithTestCases(t *testing.T) { Replace: false, }, }, + { + desc: "Type-bitmap:port", + setname: "my-test-ipset-bitmap-port", + typename: "bitmap:port", + options: IpsetCreateOptions{ + Replace: true, + Timeout: &timeout, + PortFrom: 0, + PortTo: 1024, + }, + entry: &IPSetEntry{ + Port: &port, + Replace: false, + }, + }, } for _, tC := range testCases { @@ -519,14 +534,17 @@ func TestIpsetCreateListAddDelDestroyWithTestCases(t *testing.T) { } if tC.entry.Port != nil { - if *result.Entries[0].Protocol != *tC.entry.Protocol { - t.Fatalf("expected protocol to be '%d', got '%d'", *tC.entry.Protocol, *result.Entries[0].Protocol) - } if *result.Entries[0].Port != *tC.entry.Port { t.Fatalf("expected port to be '%d', got '%d'", *tC.entry.Port, *result.Entries[0].Port) } } + if tC.entry.Protocol != nil { + if result.Entries[0].Protocol == nil || *result.Entries[0].Protocol != *tC.entry.Protocol { + t.Fatalf("expected protocol to be '%d', got '%v'", *tC.entry.Protocol, result.Entries[0].Protocol) + } + } + if tC.entry.MAC != nil { if result.Entries[0].MAC.String() != tC.entry.MAC.String() { t.Fatalf("expected mac to be '%v', got '%v'", tC.entry.MAC, result.Entries[0].MAC)