@@ -33,8 +33,21 @@ const (
3333 ApplyOnNeed IPSetMode = "on-need"
3434)
3535
36+ var (
37+ emptySetMetadata = & IPSetMetadata {
38+ Name : "emptyhashset" ,
39+ Type : EmptyHashSet ,
40+ }
41+ emptySetPrefixName = emptySetMetadata .GetPrefixName ()
42+ )
43+
3644type IPSetManager struct {
37- iMgrCfg * IPSetManagerCfg
45+ iMgrCfg * IPSetManagerCfg
46+ // emptySet is a direct reference to the empty ipset that should always be in the kernel.
47+ // This set is used based on the AddEmptySetToLists flag.
48+ // If emptySet is non-nil, it should be in the kernel or ready to be created in the dirtyCache.
49+ // Its reference counts are currently unaccounted for and may be incorrect.
50+ emptySet * IPSet
3851 setMap map [string ]* IPSet
3952 dirtyCache dirtyCacheInterface
4053 ioShim * common.IOShim
@@ -45,11 +58,16 @@ type IPSetManagerCfg struct {
4558 IPSetMode IPSetMode
4659 // NetworkName can be left empty or set to 'azure' (the only supported network)
4760 NetworkName string
61+ // AddEmptySetToLists determines whether all lists should have an empty set as a member.
62+ // This is necessary for HNS (Windows); otherwise, an allow ACL with a list condition
63+ // allows all IPs if the list has no members.
64+ AddEmptySetToLists bool
4865}
4966
5067func NewIPSetManager (iMgrCfg * IPSetManagerCfg , ioShim * common.IOShim ) * IPSetManager {
5168 return & IPSetManager {
5269 iMgrCfg : iMgrCfg ,
70+ emptySet : nil , // will be set if needed in calls to AddToLists
5371 setMap : make (map [string ]* IPSet ),
5472 dirtyCache : newDirtyCache (),
5573 ioShim : ioShim ,
@@ -60,11 +78,6 @@ func NewIPSetManager(iMgrCfg *IPSetManagerCfg, ioShim *common.IOShim) *IPSetMana
6078 Reconcile removes empty/unreferenced sets from the cache.
6179 For ApplyAllIPSets mode, those sets are added to the toDeleteCache.
6280 We can't delete from kernel immediately unless we lock iMgr during policy CRUD.
63- If this call adds a set to the toDeleteCache, and then that set is created before
64- ApplyIPSets is called, then the set may be unnecessarily added to the toAddOrUpdateCache,
65- meaning:
66- - for Linux, an unnecessary "-N set-name --exists" call would be made in the restore file
67- - for Windows, ...
6881*/
6982func (iMgr * IPSetManager ) Reconcile () {
7083 iMgr .Lock ()
@@ -86,6 +99,7 @@ func (iMgr *IPSetManager) ResetIPSets() error {
8699 metrics .ResetIPSetEntries ()
87100 err := iMgr .resetIPSets ()
88101 iMgr .setMap = make (map [string ]* IPSet )
102+ iMgr .emptySet = nil
89103 iMgr .clearDirtyCache ()
90104 if err != nil {
91105 metrics .SendErrorLogAndMetric (util .IpsmID , "error: failed to reset ipsetmanager: %s" , err .Error ())
@@ -109,12 +123,28 @@ func (iMgr *IPSetManager) createAndGetIPSet(setMetadata *IPSetMetadata) *IPSet {
109123 if exists {
110124 return set
111125 }
126+
112127 set = NewIPSet (setMetadata )
113128 iMgr .setMap [prefixedName ] = set
114129 metrics .IncNumIPSets ()
115130 if iMgr .iMgrCfg .IPSetMode == ApplyAllIPSets {
116131 iMgr .modifyCacheForKernelCreation (set )
117132 }
133+
134+ // if configured, add the empty set to lists of type KeyLabelOfNamespace and KeyValueLabelOfNamespace.
135+ // The NestedLabelOfPod list ipset type is assumed to always have a member (it is created specifically for network policy pod selectors).
136+ if iMgr .iMgrCfg .AddEmptySetToLists && (set .Type == KeyLabelOfNamespace || set .Type == KeyValueLabelOfNamespace ) {
137+ if iMgr .emptySet == nil {
138+ // duplicate of code chunk above
139+ iMgr .emptySet = NewIPSet (emptySetMetadata )
140+ iMgr .setMap [emptySetPrefixName ] = iMgr .emptySet
141+ metrics .IncNumIPSets ()
142+ iMgr .modifyCacheForKernelCreation (iMgr .emptySet )
143+ }
144+
145+ iMgr .addMemberToList (set , iMgr .emptySet )
146+ }
147+
118148 return set
119149}
120150
@@ -214,7 +244,6 @@ func (iMgr *IPSetManager) AddToSets(addToSets []*IPSetMetadata, ip, podKey strin
214244 return npmerrors .Errorf (npmerrors .AppendIPSet , true , msg )
215245 }
216246
217- // TODO check if the IP is IPV4 family in controller
218247 iMgr .Lock ()
219248 defer iMgr .Unlock ()
220249
@@ -334,19 +363,24 @@ func (iMgr *IPSetManager) AddToLists(listMetadatas, setMetadatas []*IPSetMetadat
334363 }
335364 member := iMgr .setMap [memberName ]
336365
337- iMgr .modifyCacheForKernelMemberAdd (list , member .HashedName )
338- list .MemberIPSets [memberName ] = member
339- member .incIPSetReferCount ()
340- metrics .AddEntryToIPSet (list .Name )
366+ iMgr .addMemberToList (list , member )
341367 listIsInKernel := iMgr .shouldBeInKernel (list )
342368 if listIsInKernel {
343369 iMgr .incKernelReferCountAndModifyCache (member )
344370 }
345371 }
346372 }
373+
347374 return nil
348375}
349376
377+ func (iMgr * IPSetManager ) addMemberToList (list , member * IPSet ) {
378+ iMgr .modifyCacheForKernelMemberAdd (list , member .HashedName )
379+ list .MemberIPSets [member .Name ] = member
380+ member .incIPSetReferCount ()
381+ metrics .AddEntryToIPSet (list .Name )
382+ }
383+
350384func (iMgr * IPSetManager ) RemoveFromList (listMetadata * IPSetMetadata , setMetadatas []* IPSetMetadata ) error {
351385 if len (setMetadatas ) == 0 {
352386 return nil
@@ -370,9 +404,15 @@ func (iMgr *IPSetManager) RemoveFromList(listMetadata *IPSetMetadata, setMetadat
370404 for _ , setMetadata := range setMetadatas {
371405 memberName := setMetadata .GetPrefixName ()
372406 if memberName == "" {
373- metrics .SendErrorLogAndMetric (util .IpsmID , "[RemoveFromList] warning: removing empty member name from list %s" , list .Name )
407+ metrics .SendErrorLogAndMetric (util .IpsmID , "[RemoveFromList] warning: tried to remove empty member name from list %s" , list .Name )
408+ continue
409+ }
410+
411+ if iMgr .iMgrCfg .AddEmptySetToLists && memberName == emptySetPrefixName {
412+ metrics .SendErrorLogAndMetric (util .IpsmID , "[RemoveFromList] warning: tried to remove empty set from list %s" , list .Name )
374413 continue
375414 }
415+
376416 member , exists := iMgr .setMap [memberName ]
377417 if ! exists {
378418 continue
@@ -449,21 +489,23 @@ func (iMgr *IPSetManager) exists(name string) bool {
449489
450490// the metric for number of ipsets in the kernel will be lower than in reality until the next applyIPSet call
451491func (iMgr * IPSetManager ) modifyCacheForCacheDeletion (set * IPSet , deleteOption util.DeleteOption ) {
492+ if set == iMgr .emptySet {
493+ return
494+ }
495+
452496 if deleteOption == util .ForceDelete {
453497 // If force delete, then check if Set is used by other set or network policy
454498 // else delete the set even if it has members
455499 if ! set .canBeForceDeleted () {
456500 return
457501 }
458- } else if ! set .canBeDeleted () {
502+ } else if ! set .canBeDeleted (iMgr . emptySet ) {
459503 return
460504 }
461505
462506 delete (iMgr .setMap , set .Name )
463507 metrics .DeleteIPSet (set .Name )
464508 if iMgr .iMgrCfg .IPSetMode == ApplyAllIPSets {
465- // NOTE: in ApplyAllIPSets mode, if this ipset has never been created in the kernel,
466- // it would be added to the deleteCache, and then the OS would fail to delete it
467509 iMgr .modifyCacheForKernelRemoval (set )
468510 }
469511 // if mode is ApplyOnNeed, the set will not be in the kernel (or will be in the delete cache already) since there are no references
@@ -489,7 +531,7 @@ func (iMgr *IPSetManager) incKernelReferCountAndModifyCache(member *IPSet) {
489531}
490532
491533func (iMgr * IPSetManager ) shouldBeInKernel (set * IPSet ) bool {
492- return set .shouldBeInKernel () || iMgr .iMgrCfg .IPSetMode == ApplyAllIPSets
534+ return set .shouldBeInKernel () || iMgr .iMgrCfg .IPSetMode == ApplyAllIPSets || set == iMgr . emptySet
493535}
494536
495537func (iMgr * IPSetManager ) modifyCacheForKernelRemoval (set * IPSet ) {
0 commit comments