-
Notifications
You must be signed in to change notification settings - Fork 260
Move Cidr translation from iptable to ipset. #582
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report
@@ Coverage Diff @@
## master #582 +/- ##
==========================================
+ Coverage 49.08% 53.00% +3.92%
==========================================
Files 28 23 -5
Lines 3449 2858 -591
==========================================
- Hits 1693 1515 -178
+ Misses 1466 1071 -395
+ Partials 290 272 -18 |
| func createCidrsRule(ingressOrEgress string, policyName string, ipsetEntries [][]string, ipsMgr *ipsm.IpsetManager) { | ||
| spec := append([]string{util.IpsetNetHashFlag, util.IpsetMaxelemName, util.IpsetMaxelemNum}) | ||
| for i, ipCidrSet := range ipsetEntries { | ||
| if ipCidrSet == nil || len(ipCidrSet) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
translatepolicy should already remove empty sets
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it didn't.
For the existing return object, including sets, namedPorts, lists, they are []string type and called UniqueStrSlice to remove empty set. However, for ipsetEntries, it's [][]string type and didn't do drop empty entries before returning. I intentionally didn't do that to keep index order for simplicity for deleting ipset when network policy rule updates.
|
|
||
| func removeCidrsRule(ingressOrEgress string, policyName string, ipsetEntries [][]string, ipsMgr *ipsm.IpsetManager) { | ||
| for i, ipCidrSet := range ipsetEntries { | ||
| if ipCidrSet == nil || len(ipCidrSet) == 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this func will work as intended since the length of the set includes all the entries to be deleted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
npm/nwpolicy.go
Outdated
| if ipCidrSet == nil || len(ipCidrSet) == 0 { | ||
| continue | ||
| } | ||
| setName := policyName + "-cidr" + strconv.Itoa(i) + ingressOrEgress |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setName should include namespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll add ns but remove cidr for setName.
npm/translatePolicy.go
Outdated
| resultSets = append(resultSets, ingressSets...) | ||
| resultNamedPorts = append(resultNamedPorts, ingressNamedPorts...) | ||
| resultLists = append(resultLists, ingressLists...) | ||
| resultIngressIPCidrs = ingressIPCidrs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we copy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to copy. Let me remove it.
Copied it following previous coding style but is't not needed here.
npm/translatePolicy.go
Outdated
| resultSets = append(resultSets, egressSets...) | ||
| resultNamedPorts = append(resultNamedPorts, egressNamedPorts...) | ||
| resultLists = append(resultLists, egressLists...) | ||
| resultEgressIPCidrs = egressIPCidrs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we copy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as comment in line 1515.
| resultSets = append(resultSets, ingressSets...) | ||
| resultNamedPorts = append(resultNamedPorts, ingressNamedPorts...) | ||
| resultLists = append(resultLists, ingressLists...) | ||
| resultIngressIPCidrs = ingressIPCidrs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we copy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ingressIPCidrs is a local variable which under for loop. If we want to return it outside the for loop, we have to copy it to a non-local variable. Copy it to resultIngressIPCidrs to keep consistency with existing code.
| resultSets = append(resultSets, egressSets...) | ||
| resultNamedPorts = append(resultNamedPorts, egressNamedPorts...) | ||
| resultLists = append(resultLists, egressLists...) | ||
| resultEgressIPCidrs = egressIPCidrs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we copy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
egressIPCidrs is a local variable which under for loop. If we want to return it outside the for loop, we have to copy it to a non-local variable. Copy it to resultEgressIPCidrs to keep consistency with existing code.
npm/translatePolicy.go
Outdated
| if toRule.IPBlock != nil { | ||
| if len(toRule.IPBlock.CIDR) > 0 { | ||
| ipCidrs[i] = append(ipCidrs[i], toRule.IPBlock.CIDR) | ||
| cidrIpsetName := name + "-cidr" + strconv.Itoa(i) + "out" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need for "cidr" in name to hash
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
npm/translatePolicy.go
Outdated
| if fromRule.IPBlock != nil { | ||
| if len(fromRule.IPBlock.CIDR) > 0 { | ||
| ipCidrs[i] = append(ipCidrs[i], fromRule.IPBlock.CIDR) | ||
| cidrIpsetName := name + "-cidr" + strconv.Itoa(i) + "in" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need for "-cidr", but add namespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about keeping the cidr to be more explicit? We just want this name to be unique.
At least we have to worry other ipset formed by podSelector or namespaceSelector from the same networkPolicyPeer in the future.
type NetworkPolicyPeer struct {
PodSelector *metav1.LabelSelector
NamespaceSelector *metav1.LabelSelector
IPBlock *IPBlock
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, keep cidr will make it more explicit but it's not a hard request for now. Ipset also has a limitation for the name string length.
Current NPM keeps ipset for pod and namespace already. For pod, npm uses pod label key, pod label key and value as ipset name. For namespace, upm uses the name of namespaces as ipset name. They are not conflicting with IPBlock ipset name.
npm/translatePolicy.go
Outdated
| cidrIpsetName := name + "-cidr" + strconv.Itoa(i) + "in" | ||
| if len(fromRule.IPBlock.Except) > 0 { | ||
| for _, except := range fromRule.IPBlock.Except { | ||
| ipCidrs[i] = append(ipCidrs[i], except + " " + util.IpsetNomatch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we shouldn't have no matches since we're moving to allow based style
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do it in next PR, let's have this PR focus on ip table to ip set change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a TODO comment can help move this work to our next PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me add TODO comment.
mainred
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments, but to not block your work, I'll approve your work and leave you and Jaeryn make the last decision
npm/ipsm/ipsm.go
Outdated
| operationFlag: util.IpsetCreationFlag, | ||
| set: util.GetHashedName(listName), | ||
| spec: util.IpsetSetListFlag, | ||
| spec: append([]string{util.IpsetSetListFlag}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| spec: append([]string{util.IpsetSetListFlag}), | |
| spec: []string{util.IpsetSetListFlag}, |
npm/ipsm/ipsm.go
Outdated
| operationFlag: util.IpsetAppendFlag, | ||
| set: util.GetHashedName(listName), | ||
| spec: util.GetHashedName(setName), | ||
| spec: append([]string{util.GetHashedName(setName)}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
npm/ipsm/ipsm.go
Outdated
| operationFlag: util.IpsetDeletionFlag, | ||
| set: hashedListName, | ||
| spec: hashedSetName, | ||
| spec: append([]string{hashedSetName}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
npm/nwpolicy.go
Outdated
| return nil | ||
| } | ||
|
|
||
| func createCidrsRule(ingressOrEgress string, policyName string, ipsetEntries [][]string, ipsMgr *ipsm.IpsetManager) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about moving createCidrsRule and removeCidrsRule to ipsm.IpsetManager?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about moving
createCidrsRuleandremoveCidrsRuletoipsm.IpsetManager?
createCidrsRule and removeCidrsRule are only needed during AddPolicy function which locate at nwpolicy.go. They won't be reused any where else. I would like to keep them inside nwpolicy.go. They reuse the functions of CreateSet and AddToSet in ipsm.IpsetManager. ipsm keeps more generic functions.
npm/translatePolicy.go
Outdated
| if fromRule.IPBlock != nil { | ||
| if len(fromRule.IPBlock.CIDR) > 0 { | ||
| ipCidrs[i] = append(ipCidrs[i], fromRule.IPBlock.CIDR) | ||
| cidrIpsetName := name + "-cidr" + strconv.Itoa(i) + "in" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about keeping the cidr to be more explicit? We just want this name to be unique.
At least we have to worry other ipset formed by podSelector or namespaceSelector from the same networkPolicyPeer in the future.
type NetworkPolicyPeer struct {
PodSelector *metav1.LabelSelector
NamespaceSelector *metav1.LabelSelector
IPBlock *IPBlock
}
npm/translatePolicy.go
Outdated
| cidrIpsetName := name + "-cidr" + strconv.Itoa(i) + "in" | ||
| if len(fromRule.IPBlock.Except) > 0 { | ||
| for _, except := range fromRule.IPBlock.Except { | ||
| ipCidrs[i] = append(ipCidrs[i], except + " " + util.IpsetNomatch) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a TODO comment can help move this work to our next PR.
npm/translatePolicy.go
Outdated
| } | ||
|
|
||
| func translateIngress(ns string, targetSelector metav1.LabelSelector, rules []networkingv1.NetworkPolicyIngressRule) ([]string, []string, []string, []*iptm.IptEntry) { | ||
| func translateIngress(ns string, name string, targetSelector metav1.LabelSelector, rules []networkingv1.NetworkPolicyIngressRule) ([]string, []string, []string, [][]string, []*iptm.IptEntry) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
policyName to be more explicit?
| } | ||
| addedIngressFromEntry = true | ||
| } | ||
| if j != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides IPBLocks, we also have podselector and namespace selector in the following steps, so we cannot continue to next rule item.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may later refactor this lengthy function to be more clean and easy to maintain.
CC @jaer-tsun
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Besides IPBLocks, we also have podselector and namespace selector in the following steps, so we cannot continue to next rule item.
We can continue. Please note the continue condition is when j != 0 which means the podselector and namespace selector has already been added in iptable entry when j = 0. It's a key point to improve our performance. When j = 0 which means npm loop through the 1st item in ipBlock, npm will create a cidr ipset, create a iptable entry. When j > 0, npm just need to add cidr to ipset entry, no need to create iptable entries anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may later refactor this lengthy function to be more clean and easy to maintain.
CC @jaer-tsun
Agree, I drafted a PR to refactor translatePolicy.go to remove duplicated code between translateIngress and translateEgress and make it concise.
npm/translatePolicy.go
Outdated
| } | ||
|
|
||
| func translateEgress(ns string, targetSelector metav1.LabelSelector, rules []networkingv1.NetworkPolicyEgressRule) ([]string, []string, []string, []*iptm.IptEntry) { | ||
| func translateEgress(ns string, name string, targetSelector metav1.LabelSelector, rules []networkingv1.NetworkPolicyEgressRule) ([]string, []string, []string, [][]string, []*iptm.IptEntry) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| func translateEgress(ns string, name string, targetSelector metav1.LabelSelector, rules []networkingv1.NetworkPolicyEgressRule) ([]string, []string, []string, [][]string, []*iptm.IptEntry) { | |
| func translateEgress(ns string, policyName string, targetSelector metav1.LabelSelector, rules []networkingv1.NetworkPolicyEgressRule) ([]string, []string, []string, [][]string, []*iptm.IptEntry) { |
mainred
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gofmt is also needed.
Let me open a separate PR for gofmt. |
jaer-tsun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm

What this PR does / why we need it:
Ipset is an extension for iptable to store and look up ip blocks in an efficient hash way. Move ip cidr related rule to ipset will improve npm performance.
Which issue this PR fixes
It fixes perf issue when large amount of ip cidr rule got applied.
Special notes for your reviewer:
Examples of ipset and iptables rules with this change.
Release note: