Skip to content

Commit a47e0f4

Browse files
jimmy-zhmurali-reddy
authored andcommitted
Add support for 'except' feature of network policy rule (#543)
* add support for 'except' feature in NPC * support CIDR with zero prefix size in NPC
1 parent 05907d8 commit a47e0f4

File tree

2 files changed

+71
-21
lines changed

2 files changed

+71
-21
lines changed

pkg/controllers/netpol/network_policy_controller.go

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ type ingressRule struct {
104104
ports []protocolAndPort
105105
matchAllSource bool
106106
srcPods []podInfo
107-
cidrs []string
107+
srcIPBlocks [][]string
108108
}
109109

110110
// internal structure to represent NetworkPolicyEgressRule in the spec
@@ -113,7 +113,7 @@ type egressRule struct {
113113
ports []protocolAndPort
114114
matchAllDestinations bool
115115
dstPods []podInfo
116-
cidrs []string
116+
dstIPBlocks [][]string
117117
}
118118

119119
type protocolAndPort struct {
@@ -429,20 +429,20 @@ func (npc *NetworkPolicyController) processIngressRules(policy networkPolicyInfo
429429
}
430430
}
431431

432-
if len(ingressRule.cidrs) != 0 {
432+
if len(ingressRule.srcIPBlocks) != 0 {
433433
srcIpBlockIpSetName := policyIndexedSourceIpBlockIpSetName(policy.namespace, policy.name, i)
434434
srcIpBlockIpSet, err := npc.ipSetHandler.Create(srcIpBlockIpSetName, utils.TypeHashNet, utils.OptionTimeout, "0")
435435
if err != nil {
436436
return fmt.Errorf("failed to create ipset: %s", err.Error())
437437
}
438438
activePolicyIpSets[srcIpBlockIpSet.Name] = true
439-
err = srcIpBlockIpSet.Refresh(ingressRule.cidrs, utils.OptionTimeout, "0")
439+
err = srcIpBlockIpSet.RefreshWithBuiltinOptions(ingressRule.srcIPBlocks)
440440
if err != nil {
441441
glog.Errorf("failed to refresh srcIpBlockIpSet: " + err.Error())
442442
}
443443
if !ingressRule.matchAllPorts {
444444
for _, portProtocol := range ingressRule.ports {
445-
comment := "rule to ACCEPT traffic from specified CIDR to dest pods selected by policy name: " +
445+
comment := "rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: " +
446446
policy.name + " namespace " + policy.namespace
447447
args := []string{"-m", "comment", "--comment", comment,
448448
"-m", "set", "--set", srcIpBlockIpSetName, "src",
@@ -457,7 +457,7 @@ func (npc *NetworkPolicyController) processIngressRules(policy networkPolicyInfo
457457
}
458458
}
459459
if ingressRule.matchAllPorts {
460-
comment := "rule to ACCEPT traffic from specified CIDR to dest pods selected by policy name: " +
460+
comment := "rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: " +
461461
policy.name + " namespace " + policy.namespace
462462
args := []string{"-m", "comment", "--comment", comment,
463463
"-m", "set", "--set", srcIpBlockIpSetName, "src",
@@ -573,20 +573,20 @@ func (npc *NetworkPolicyController) processEgressRules(policy networkPolicyInfo,
573573
return fmt.Errorf("Failed to run iptables command: %s", err.Error())
574574
}
575575
}
576-
if len(egressRule.cidrs) != 0 {
576+
if len(egressRule.dstIPBlocks) != 0 {
577577
dstIpBlockIpSetName := policyIndexedDestinationIpBlockIpSetName(policy.namespace, policy.name, i)
578578
dstIpBlockIpSet, err := npc.ipSetHandler.Create(dstIpBlockIpSetName, utils.TypeHashNet, utils.OptionTimeout, "0")
579579
if err != nil {
580580
return fmt.Errorf("failed to create ipset: %s", err.Error())
581581
}
582582
activePolicyIpSets[dstIpBlockIpSet.Name] = true
583-
err = dstIpBlockIpSet.Refresh(egressRule.cidrs, utils.OptionTimeout, "0")
583+
err = dstIpBlockIpSet.RefreshWithBuiltinOptions(egressRule.dstIPBlocks)
584584
if err != nil {
585585
glog.Errorf("failed to refresh dstIpBlockIpSet: " + err.Error())
586586
}
587587
if !egressRule.matchAllPorts {
588588
for _, portProtocol := range egressRule.ports {
589-
comment := "rule to ACCEPT traffic from source pods to specified CIDR selected by policy name: " +
589+
comment := "rule to ACCEPT traffic from source pods to specified ipBlocks selected by policy name: " +
590590
policy.name + " namespace " + policy.namespace
591591
args := []string{"-m", "comment", "--comment", comment,
592592
"-m", "set", "--set", targetSourcePodIpSetName, "src",
@@ -601,7 +601,7 @@ func (npc *NetworkPolicyController) processEgressRules(policy networkPolicyInfo,
601601
}
602602
}
603603
if egressRule.matchAllPorts {
604-
comment := "rule to ACCEPT traffic from source pods to specified CIDR selected by policy name: " +
604+
comment := "rule to ACCEPT traffic from source pods to specified ipBlocks selected by policy name: " +
605605
policy.name + " namespace " + policy.namespace
606606
args := []string{"-m", "comment", "--comment", comment,
607607
"-m", "set", "--set", targetSourcePodIpSetName, "src",
@@ -1128,7 +1128,7 @@ func (npc *NetworkPolicyController) buildNetworkPoliciesInfo() (*[]networkPolicy
11281128
}
11291129

11301130
ingressRule.srcPods = make([]podInfo, 0)
1131-
ingressRule.cidrs = make([]string, 0)
1131+
ingressRule.srcIPBlocks = make([][]string, 0)
11321132

11331133
// If this field is empty or missing in the spec, this rule matches all sources
11341134
if len(specIngressRule.From) == 0 {
@@ -1137,11 +1137,9 @@ func (npc *NetworkPolicyController) buildNetworkPoliciesInfo() (*[]networkPolicy
11371137
ingressRule.matchAllSource = false
11381138
var matchingPods []*api.Pod
11391139
for _, peer := range specIngressRule.From {
1140-
peerPods, err := npc.evalPeer(policy, peer)
1140+
peerPods, err := npc.evalPodPeer(policy, peer)
11411141
matchingPods = append(matchingPods, peerPods...)
1142-
if peer.PodSelector == nil && peer.NamespaceSelector == nil && peer.IPBlock != nil {
1143-
ingressRule.cidrs = append(ingressRule.cidrs, peer.IPBlock.CIDR)
1144-
}
1142+
ingressRule.srcIPBlocks = append(ingressRule.srcIPBlocks, npc.evalIPBlockPeer(peer)...)
11451143
if err == nil {
11461144
for _, matchingPod := range matchingPods {
11471145
if matchingPod.Status.PodIP == "" {
@@ -1177,7 +1175,7 @@ func (npc *NetworkPolicyController) buildNetworkPoliciesInfo() (*[]networkPolicy
11771175
}
11781176

11791177
egressRule.dstPods = make([]podInfo, 0)
1180-
egressRule.cidrs = make([]string, 0)
1178+
egressRule.dstIPBlocks = make([][]string, 0)
11811179

11821180
// If this field is empty or missing in the spec, this rule matches all sources
11831181
if len(specEgressRule.To) == 0 {
@@ -1186,11 +1184,9 @@ func (npc *NetworkPolicyController) buildNetworkPoliciesInfo() (*[]networkPolicy
11861184
egressRule.matchAllDestinations = false
11871185
var matchingPods []*api.Pod
11881186
for _, peer := range specEgressRule.To {
1189-
peerPods, err := npc.evalPeer(policy, peer)
1187+
peerPods, err := npc.evalPodPeer(policy, peer)
11901188
matchingPods = append(matchingPods, peerPods...)
1191-
if peer.PodSelector == nil && peer.NamespaceSelector == nil && peer.IPBlock != nil {
1192-
egressRule.cidrs = append(egressRule.cidrs, peer.IPBlock.CIDR)
1193-
}
1189+
egressRule.dstIPBlocks = append(egressRule.dstIPBlocks, npc.evalIPBlockPeer(peer)...)
11941190
if err == nil {
11951191
for _, matchingPod := range matchingPods {
11961192
egressRule.dstPods = append(egressRule.dstPods,
@@ -1211,7 +1207,7 @@ func (npc *NetworkPolicyController) buildNetworkPoliciesInfo() (*[]networkPolicy
12111207
return &NetworkPolicies, nil
12121208
}
12131209

1214-
func (npc *NetworkPolicyController) evalPeer(policy *networking.NetworkPolicy, peer networking.NetworkPolicyPeer) ([]*api.Pod, error) {
1210+
func (npc *NetworkPolicyController) evalPodPeer(policy *networking.NetworkPolicy, peer networking.NetworkPolicyPeer) ([]*api.Pod, error) {
12151211

12161212
var matchingPods []*api.Pod
12171213
matchingPods = make([]*api.Pod, 0)
@@ -1259,6 +1255,25 @@ func (npc *NetworkPolicyController) ListNamespaceByLabels(set labels.Set) ([]*ap
12591255
return matchedNamespaces, nil
12601256
}
12611257

1258+
func (npc *NetworkPolicyController) evalIPBlockPeer(peer networking.NetworkPolicyPeer) [][]string {
1259+
ipBlock := make([][]string, 0)
1260+
if peer.PodSelector == nil && peer.NamespaceSelector == nil && peer.IPBlock != nil {
1261+
if cidr := peer.IPBlock.CIDR; strings.HasSuffix(cidr, "/0") {
1262+
ipBlock = append(ipBlock, []string{"0.0.0.0/1", utils.OptionTimeout, "0"}, []string{"128.0.0.0/1", utils.OptionTimeout, "0"})
1263+
} else {
1264+
ipBlock = append(ipBlock, []string{cidr, utils.OptionTimeout, "0"})
1265+
}
1266+
for _, except := range peer.IPBlock.Except {
1267+
if strings.HasSuffix(except, "/0") {
1268+
ipBlock = append(ipBlock, []string{"0.0.0.0/1", utils.OptionTimeout, "0", utils.OptionNoMatch}, []string{"128.0.0.0/1", utils.OptionTimeout, "0", utils.OptionNoMatch})
1269+
} else {
1270+
ipBlock = append(ipBlock, []string{except, utils.OptionTimeout, "0", utils.OptionNoMatch})
1271+
}
1272+
}
1273+
}
1274+
return ipBlock
1275+
}
1276+
12621277
func (npc *NetworkPolicyController) buildBetaNetworkPoliciesInfo() (*[]networkPolicyInfo, error) {
12631278

12641279
NetworkPolicies := make([]networkPolicyInfo, 0)

pkg/utils/ipset.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,3 +472,38 @@ func (set *Set) Refresh(entries []string, extraOptions ...string) error {
472472

473473
return nil
474474
}
475+
476+
// Refresh a Set with new entries with built-in options.
477+
func (set *Set) RefreshWithBuiltinOptions(entries [][]string) error {
478+
var err error
479+
tempName := set.Name + "-temp"
480+
newSet := &Set{
481+
Parent: set.Parent,
482+
Name: tempName,
483+
Options: set.Options,
484+
}
485+
486+
err = set.Parent.Add(newSet)
487+
if err != nil {
488+
return err
489+
}
490+
491+
for _, entry := range entries {
492+
_, err = newSet.Add(entry...)
493+
if err != nil {
494+
return err
495+
}
496+
}
497+
498+
err = set.Swap(newSet)
499+
if err != nil {
500+
return err
501+
}
502+
503+
err = set.Parent.Destroy(tempName)
504+
if err != nil {
505+
return err
506+
}
507+
508+
return nil
509+
}

0 commit comments

Comments
 (0)