npm/pkg/dataplane/ipsets/ipset.go (303 lines of code) (raw):
package ipsets
import (
"errors"
"fmt"
"github.com/Azure/azure-container-networking/log"
"github.com/Azure/azure-container-networking/npm/metrics"
"github.com/Azure/azure-container-networking/npm/util"
)
type IPSetMetadata struct {
Name string
Type SetType
}
type SetKind string
const (
// ListSet is of kind list with members as other IPSets
ListSet SetKind = "list"
// HashSet is of kind hashset with members as IPs and/or port
HashSet SetKind = "set"
// UnknownKind is returned when kind is unknown
UnknownKind SetKind = "unknown"
)
// NewIPSetMetadata is used for controllers to send in skeleton ipsets to DP
func NewIPSetMetadata(name string, setType SetType) *IPSetMetadata {
set := &IPSetMetadata{
Name: name,
Type: setType,
}
return set
}
func (setMetadata *IPSetMetadata) GetHashedName() string {
prefixedName := setMetadata.GetPrefixName()
if prefixedName == Unknown {
return Unknown
}
return util.GetHashedName(prefixedName)
}
// TODO join with colon instead of dash for easier readability?
func (setMetadata *IPSetMetadata) GetPrefixName() string {
switch setMetadata.Type {
case CIDRBlocks:
return fmt.Sprintf("%s%s", util.CIDRPrefix, setMetadata.Name)
case Namespace:
return fmt.Sprintf("%s%s", util.NamespacePrefix, setMetadata.Name)
case NamedPorts:
return fmt.Sprintf("%s%s", util.NamedPortIPSetPrefix, setMetadata.Name)
case KeyLabelOfPod:
return fmt.Sprintf("%s%s", util.PodLabelPrefix, setMetadata.Name)
case KeyValueLabelOfPod:
return fmt.Sprintf("%s%s", util.PodLabelPrefix, setMetadata.Name)
case KeyLabelOfNamespace:
return fmt.Sprintf("%s%s", util.NamespaceLabelPrefix, setMetadata.Name)
case KeyValueLabelOfNamespace:
return fmt.Sprintf("%s%s", util.NamespaceLabelPrefix, setMetadata.Name)
case NestedLabelOfPod:
return fmt.Sprintf("%s%s", util.NestedLabelPrefix, setMetadata.Name)
case EmptyHashSet:
return fmt.Sprintf("%s%s", util.EmptySetPrefix, setMetadata.Name)
case UnknownType: // adding this to appease golint
metrics.SendErrorLogAndMetric(util.UtilID, "experienced unknown type in set metadata: %+v", setMetadata)
return Unknown
default:
metrics.SendErrorLogAndMetric(util.UtilID, "experienced unexpected type %d in set metadata: %+v", setMetadata.Type, setMetadata)
return Unknown
}
}
func (setMetadata *IPSetMetadata) GetSetKind() SetKind {
return setMetadata.Type.getSetKind()
}
func (setType SetType) getSetKind() SetKind {
switch setType {
case CIDRBlocks:
return HashSet
case Namespace:
return HashSet
case NamedPorts:
return HashSet
case KeyLabelOfPod:
return HashSet
case KeyValueLabelOfPod:
return HashSet
case EmptyHashSet:
return HashSet
case KeyLabelOfNamespace:
return ListSet
case KeyValueLabelOfNamespace:
return ListSet
case NestedLabelOfPod:
return ListSet
case UnknownType: // adding this to appease golint
return UnknownKind
default:
return UnknownKind
}
}
// TranslatedIPSet is created by translation engine and provides IPSets used in
// network policy. Only 2 types of IPSets are generated with members:
// 1. CIDRBlocks IPSet
// 2. NestedLabelOfPod IPSet from multi value labels
// Members field holds member ipset names for NestedLabelOfPod and ip address ranges
// for CIDRBlocks IPSet
// Caveat: if a list set with translated members is referenced in multiple policies,
// then it must have a different ipset name for each policy. Otherwise, deleting the policy
// will result in removing the translated members from the set even if another policy requires
// those members. See dataplane.go for more details.
type TranslatedIPSet struct {
Metadata *IPSetMetadata
// Members holds member ipset names for NestedLabelOfPod and ip address ranges
// for CIDRBlocks IPSet
Members []string
}
// NewTranslatedIPSet creates TranslatedIPSet.
// Only nested labels from podSelector and IPBlock has members and others has nil slice.
func NewTranslatedIPSet(name string, setType SetType, members ...string) *TranslatedIPSet {
translatedIPSet := &TranslatedIPSet{
Metadata: NewIPSetMetadata(name, setType),
Members: members,
}
return translatedIPSet
}
type SetProperties struct {
// Stores type of ip grouping
Type SetType
// Stores kind of ipset in dataplane
Kind SetKind
}
type SetType int8
// Possble values for SetType
const (
// Unknown SetType
UnknownType SetType = 0
// Namespace IPSet is created to hold
// ips of pods in a given NameSapce
Namespace SetType = 1
// KeyLabelOfNamespace IPSet is a list kind ipset
// with members as ipsets of namespace with this Label Key
KeyLabelOfNamespace SetType = 2
// KeyValueLabelOfNamespace IPSet is a list kind ipset
// with members as ipsets of namespace with this Label
KeyValueLabelOfNamespace SetType = 3
// KeyLabelOfPod IPSet contains IPs of Pods with this Label Key
KeyLabelOfPod SetType = 4
// KeyValueLabelOfPod IPSet contains IPs of Pods with this Label
KeyValueLabelOfPod SetType = 5
// NamedPorts IPSets contains a given namedport
NamedPorts SetType = 6
// NestedLabelOfPod is derived for multivalue matchexpressions
NestedLabelOfPod SetType = 7
// CIDRBlocks holds CIDR blocks
CIDRBlocks SetType = 8
// EmptyHashSet is a set meant to have no members
EmptyHashSet SetType = 9
// Unknown const for unknown string
Unknown string = "unknown"
)
var (
setTypeName = map[SetType]string{
UnknownType: Unknown,
Namespace: "Namespace",
KeyLabelOfNamespace: "KeyLabelOfNamespace",
KeyValueLabelOfNamespace: "KeyValueLabelOfNamespace",
KeyLabelOfPod: "KeyLabelOfPod",
KeyValueLabelOfPod: "KeyValueLabelOfPod",
NamedPorts: "NamedPorts",
NestedLabelOfPod: "NestedLabelOfPod",
CIDRBlocks: "CIDRBlocks",
EmptyHashSet: "EmptySet",
}
// ErrIPSetInvalidKind is returned when IPSet kind is invalid
ErrIPSetInvalidKind = errors.New("invalid IPSet Kind")
)
func (x SetType) String() string {
return setTypeName[x]
}
// ReferenceType specifies the kind of reference for an IPSet
type ReferenceType string
// Possible ReferenceTypes
const (
SelectorType ReferenceType = "Selector"
NetPolType ReferenceType = "NetPol"
)
type IPSet struct {
// Name is prefixed name of original set
Name string
unprefixedName string
// HashedName is AzureNpmPrefix (azure-npm-) + hash of prefixed name
HashedName string
// SetProperties embedding set properties
SetProperties
// IpPodKey is used for setMaps to store Ips and ports as keys
// and podKey as value
IPPodKey map[string]string
// This is used for listMaps to store child IP Sets
MemberIPSets map[string]*IPSet
// Using a map to emulate set and value as struct{} for
// minimal memory consumption
// SelectorReference holds networkpolicy names where this IPSet
// is being used in PodSelector and Namespace
SelectorReference map[string]struct{}
// NetPolReference holds networkpolicy names where this IPSet
// is being referred as part of rules
NetPolReference map[string]struct{}
// ipsetReferCount keeps track of how many lists in the cache refer to this ipset
ipsetReferCount int
// kernelReferCount keeps track of how many lists in the kernel refer to this ipset
kernelReferCount int
}
func NewIPSet(setMetadata *IPSetMetadata) *IPSet {
prefixedName := setMetadata.GetPrefixName()
set := &IPSet{
Name: prefixedName,
unprefixedName: setMetadata.Name,
HashedName: util.GetHashedName(prefixedName),
SetProperties: SetProperties{
Type: setMetadata.Type,
Kind: setMetadata.GetSetKind(),
},
// Map with Key as Network Policy name to to emulate set
// and value as struct{} for minimal memory consumption
SelectorReference: make(map[string]struct{}),
// Map with Key as Network Policy name to to emulate set
// and value as struct{} for minimal memory consumption
NetPolReference: make(map[string]struct{}),
ipsetReferCount: 0,
kernelReferCount: 0,
}
if set.Kind == HashSet {
set.IPPodKey = make(map[string]string)
} else {
set.MemberIPSets = make(map[string]*IPSet)
}
return set
}
// GetSetMetadata returns set metadata with unprefixed original name and SetType
func (set *IPSet) GetSetMetadata() *IPSetMetadata {
return NewIPSetMetadata(set.unprefixedName, set.Type)
}
func (set *IPSet) PrettyString() string {
return fmt.Sprintf("Name: %s HashedNamed: %s Type: %s Kind: %s",
set.Name, set.HashedName, setTypeName[set.Type], string(set.Kind))
}
// GetSetContents returns members of set as string slice
func (set *IPSet) GetSetContents() ([]string, error) {
switch set.Kind {
case HashSet:
i := 0
contents := make([]string, len(set.IPPodKey))
for podIP := range set.IPPodKey {
contents[i] = podIP
i++
}
return contents, nil
case ListSet:
i := 0
contents := make([]string, len(set.MemberIPSets))
for _, memberSet := range set.MemberIPSets {
contents[i] = memberSet.HashedName
i++
}
return contents, nil
default:
return []string{}, ErrIPSetInvalidKind
}
}
// ShallowCompare check if the properties of IPSets are same
func (set *IPSet) ShallowCompare(newSet *IPSet) bool {
if set.Name != newSet.Name {
return false
}
if set.Kind != newSet.Kind {
return false
}
if set.Type != newSet.Type {
return false
}
return true
}
func (set *IPSet) incIPSetReferCount() {
set.ipsetReferCount++
}
func (set *IPSet) decIPSetReferCount() {
if set.ipsetReferCount == 0 {
return
}
set.ipsetReferCount--
}
func (set *IPSet) incKernelReferCount() {
set.kernelReferCount++
}
func (set *IPSet) decKernelReferCount() {
if set.kernelReferCount == 0 {
return
}
set.kernelReferCount--
}
func (set *IPSet) addReference(referenceName string, referenceType ReferenceType) {
switch referenceType {
case SelectorType:
set.SelectorReference[referenceName] = struct{}{}
case NetPolType:
set.NetPolReference[referenceName] = struct{}{}
default:
log.Logf("IPSet_addReference: encountered unknown ReferenceType")
}
}
func (set *IPSet) deleteReference(referenceName string, referenceType ReferenceType) {
switch referenceType {
case SelectorType:
delete(set.SelectorReference, referenceName)
case NetPolType:
delete(set.NetPolReference, referenceName)
default:
log.Logf("IPSet_deleteReference: encountered unknown ReferenceType")
}
}
func (set *IPSet) shouldBeInKernel() bool {
return set.usedByNetPol() || set.referencedInKernel()
}
func (set *IPSet) canBeForceDeleted() bool {
return !set.usedByNetPol() &&
!set.referencedInList()
}
func (set *IPSet) canBeDeleted(ignorableMember *IPSet) bool {
var firstMember *IPSet
for _, member := range set.MemberIPSets {
firstMember = member
break
}
listMembersOk := len(set.MemberIPSets) == 0 || (len(set.MemberIPSets) == 1 && firstMember == ignorableMember)
return !set.usedByNetPol() &&
!set.referencedInList() &&
len(set.IPPodKey) == 0 &&
listMembersOk
}
// usedByNetPol check if an IPSet is referred in network policies.
func (set *IPSet) usedByNetPol() bool {
return len(set.SelectorReference) > 0 ||
len(set.NetPolReference) > 0
}
func (set *IPSet) referencedInList() bool {
return set.ipsetReferCount > 0
}
func (set *IPSet) referencedInKernel() bool {
return set.kernelReferCount > 0
}
// panics if set is not a list set
func (set *IPSet) hasMember(memberName string) bool {
_, isMember := set.MemberIPSets[memberName]
return isMember
}
func (set *IPSet) canSetBeSelectorIPSet() bool {
return (set.Type == KeyLabelOfPod ||
set.Type == KeyValueLabelOfPod ||
set.Type == Namespace ||
set.Type == NestedLabelOfPod)
}
func GetMembersOfTranslatedSets(members []string) []*IPSetMetadata {
memberList := make([]*IPSetMetadata, len(members))
i := 0
for _, setName := range members {
// translate engine only returns KeyValueLabelOfPod as member
memberSet := NewIPSetMetadata(setName, KeyValueLabelOfPod)
memberList[i] = memberSet
i++
}
return memberList
}