@@ -147,9 +147,11 @@ func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOption
147147 req .AddData (nl .NewRtAttr (nl .IPSET_ATTR_SETNAME , nl .ZeroTerminated (setname )))
148148 req .AddData (nl .NewRtAttr (nl .IPSET_ATTR_TYPENAME , nl .ZeroTerminated (typename )))
149149
150+ cadtFlags := optionsToBitflag (options )
151+
150152 revision := options .Revision
151153 if revision == 0 {
152- revision = getIpsetDefaultWithTypeName (typename )
154+ revision = getIpsetDefaultRevision (typename , cadtFlags )
153155 }
154156 req .AddData (nl .NewRtAttr (nl .IPSET_ATTR_REVISION , nl .Uint8Attr (revision )))
155157
@@ -181,18 +183,6 @@ func (h *Handle) IpsetCreate(setname, typename string, options IpsetCreateOption
181183 data .AddChild (& nl.Uint32Attribute {Type : nl .IPSET_ATTR_TIMEOUT | nl .NLA_F_NET_BYTEORDER , Value : * timeout })
182184 }
183185
184- var cadtFlags uint32
185-
186- if options .Comments {
187- cadtFlags |= nl .IPSET_FLAG_WITH_COMMENT
188- }
189- if options .Counters {
190- cadtFlags |= nl .IPSET_FLAG_WITH_COUNTERS
191- }
192- if options .Skbinfo {
193- cadtFlags |= nl .IPSET_FLAG_WITH_SKBINFO
194- }
195-
196186 if cadtFlags != 0 {
197187 data .AddChild (& nl.Uint32Attribute {Type : nl .IPSET_ATTR_CADT_FLAGS | nl .NLA_F_NET_BYTEORDER , Value : cadtFlags })
198188 }
@@ -395,14 +385,89 @@ func (h *Handle) newIpsetRequest(cmd int) *nl.NetlinkRequest {
395385 return req
396386}
397387
398- func getIpsetDefaultWithTypeName (typename string ) uint8 {
388+ // NOTE: This can't just take typename into account, it also has to take desired
389+ // feature support into account, on a per-set-type basis, to return the correct revision, see e.g.
390+ // https://github.com/Olipro/ipset/blob/9f145b49100104d6570fe5c31a5236816ebb4f8f/kernel/net/netfilter/ipset/ip_set_hash_ipport.c#L30
391+ //
392+ // This means that whenever a new "type" of ipset is added, returning the "correct" default revision
393+ // requires adding a new case here for that type, and consulting the ipset C code to figure out the correct
394+ // combination of type name, feature bit flags, and revision ranges.
395+ //
396+ // Care should be taken as some types share the same revision ranges for the same features, and others do not.
397+ // When in doubt, mimic the C code.
398+ func getIpsetDefaultRevision (typename string , featureFlags uint32 ) uint8 {
399399 switch typename {
400400 case "hash:ip,port" ,
401- "hash:ip,port,ip" ,
402- "hash:ip,port,net" ,
401+ "hash:ip,port,ip" :
402+ // Taken from
403+ // - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipport.c
404+ // - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipportip.c
405+ if (featureFlags & nl .IPSET_FLAG_WITH_SKBINFO ) != 0 {
406+ return 5
407+ }
408+
409+ if (featureFlags & nl .IPSET_FLAG_WITH_FORCEADD ) != 0 {
410+ return 4
411+ }
412+
413+ if (featureFlags & nl .IPSET_FLAG_WITH_COMMENT ) != 0 {
414+ return 3
415+ }
416+
417+ if (featureFlags & nl .IPSET_FLAG_WITH_COUNTERS ) != 0 {
418+ return 2
419+ }
420+
421+ // the min revision this library supports for this type
422+ return 1
423+
424+ case "hash:ip,port,net" ,
403425 "hash:net,port" :
426+ // Taken from
427+ // - ipset/kernel/net/netfilter/ipset/ip_set_hash_ipportnet.c
428+ // - ipset/kernel/net/netfilter/ipset/ip_set_hash_netport.c
429+ if (featureFlags & nl .IPSET_FLAG_WITH_SKBINFO ) != 0 {
430+ return 7
431+ }
432+
433+ if (featureFlags & nl .IPSET_FLAG_WITH_FORCEADD ) != 0 {
434+ return 6
435+ }
436+
437+ if (featureFlags & nl .IPSET_FLAG_WITH_COMMENT ) != 0 {
438+ return 5
439+ }
440+
441+ if (featureFlags & nl .IPSET_FLAG_WITH_COUNTERS ) != 0 {
442+ return 4
443+ }
444+
445+ if (featureFlags & nl .IPSET_FLAG_NOMATCH ) != 0 {
446+ return 3
447+ }
448+ // the min revision this library supports for this type
449+ return 2
450+
451+ case "hash:ip" :
452+ // Taken from
453+ // - ipset/kernel/net/netfilter/ipset/ip_set_hash_ip.c
454+ if (featureFlags & nl .IPSET_FLAG_WITH_SKBINFO ) != 0 {
455+ return 4
456+ }
457+
458+ if (featureFlags & nl .IPSET_FLAG_WITH_FORCEADD ) != 0 {
459+ return 3
460+ }
461+
462+ if (featureFlags & nl .IPSET_FLAG_WITH_COMMENT ) != 0 {
463+ return 2
464+ }
465+
466+ // the min revision this library supports for this type
404467 return 1
405468 }
469+
470+ // can't map the correct revision for this type.
406471 return 0
407472}
408473
@@ -579,3 +644,19 @@ func parseIPSetEntry(data []byte) (entry IPSetEntry) {
579644 }
580645 return
581646}
647+
648+ func optionsToBitflag (options IpsetCreateOptions ) uint32 {
649+ var cadtFlags uint32
650+
651+ if options .Comments {
652+ cadtFlags |= nl .IPSET_FLAG_WITH_COMMENT
653+ }
654+ if options .Counters {
655+ cadtFlags |= nl .IPSET_FLAG_WITH_COUNTERS
656+ }
657+ if options .Skbinfo {
658+ cadtFlags |= nl .IPSET_FLAG_WITH_SKBINFO
659+ }
660+
661+ return cadtFlags
662+ }
0 commit comments