-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Group common sub-sequences of policies into shared iptables chains #8098
Conversation
bd5397a
to
663aac1
Compare
- Recalculate groups on seelctor change. - Embed direction in group so that we get unique groups for each direction.
db1c91a
to
67894db
Compare
Combine ClearMark rules into one.
7db0210
to
0a95fd4
Compare
Various fixes.
0a95fd4
to
703c1cc
Compare
dd2b132
to
eeb4b46
Compare
Instead of refcounting blindly, trigger incref of children only when the chain itself becomes referenced. Trigger decref only for referenced chains. Only invalidate dataplane if changing a referenced chain.
eeb4b46
to
b360b6a
Compare
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.
Some small points, but all up to you and I don't think I will need to re-review. Please LMK if any of the points I've mentioned are not clear.
@@ -244,6 +244,8 @@ message Policy { | |||
repeated Rule outbound_rules = 2; | |||
bool untracked = 3; | |||
bool pre_dnat = 4; | |||
|
|||
string original_selector = 6; |
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.
(Assume the numbering here is because there's an = 5
field in Enterprise.)
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.
5 is used above for namespace.
} else { | ||
break | ||
} | ||
} |
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.
It would be marginally nicer if the coding here made it clear that these rules could not be further appended to. I'm aware that that is in fact the case - by looking at where ProtoRulesToIptablesRules is used - but I wonder if it might be worth changing the signature so that this function here returns the entire &iptables.Chain?
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 coded this up but it made the tests uglier. Right now they can treat this as simple one-in-one-out function. Returning a chain means we have to pass in a chain name and then unpack the rules to test them.
const ( | ||
PolicyDirectionIngress PolicyDirection = "ingress" | ||
PolicyDirectionEgress PolicyDirection = "egress" | ||
) |
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'm amazed we don't have a definition like this somewhere already!
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 couldn't find one!
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.
Have changed these to match the terminology used in this package inbound/outbound
felix/rules/endpoints.go
Outdated
@@ -254,14 +258,14 @@ func (r *DefaultRuleRenderer) HostEndpointToRawChains( | |||
|
|||
func (r *DefaultRuleRenderer) HostEndpointToMangleIngressChains( | |||
ifaceName string, | |||
preDNATPolicyNames []string, | |||
preDNATPolicis []*PolicyGroup, |
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.
sp, should be "...ies"
polChainPrefix := PolicyInboundPfx | ||
if group.Direction == PolicyDirectionEgress { | ||
polChainPrefix = PolicyOutboundPfx | ||
} |
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.
For host endpoints, are the "i" and "o" prefixes here the same way round as they currently are for policies applying to a host endpoint? (Might be a bit confusing if not.)
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.
Oh, and same question (albeit not specifically this bit of code) for the policy group "gi" and "go" prefixes.
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.
The pi and gi chains line up, as do the po and go chains. I.e. gi chains are groups of pi policies. Policy inbound/outbound aligns with the policy model direction. It's the to/from endpoint chains that are flipped between HEPs and WEPs.
write(strconv.Itoa(len(g.PolicyNames))) | ||
for _, name := range g.PolicyNames { | ||
write(name) | ||
} |
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.
Can you comment (and remind me) why we include tier and selector here, as well as the direction and policy names? If we had the same group in different tiers, couldn't we reuse the same group?
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.
- Tier: needed (in enterprise) to disambiguate tierA/policy1 vs tierB/policy1; different policies with the same policy name.
- Direction and Selector I found useful to make the object self describing.
Tier needs to be in the hash. Direction and selector aren't strictly needed in the hash (I think) but I was finding it easier to prove to myself that all bases were covered if I included them rather than try to prove tat it works without them In an earlier iteration I didn't have the gi/go prefix, so the direction was needed.
ifaceNameToPolicyGroupChainNames map[string][]string /*chain name*/ | ||
|
||
activePolicySelectors map[proto.PolicyID]string | ||
policyChainRefCounts map[string] /*chain name*/ int |
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.
Worry that the separation of int
here will be confusing.
break hepLoop | ||
} | ||
} | ||
} |
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.
Possibly worth a common utility definition here:
tierUsesDirtyPolicy := func(t *proto.TierInfo) bool {
for _, pols := range [][]string{t.IngressPolicies, t.EgressPolicies} {
for _, p := range pols {
polID := proto.PolicyID{
Tier: t.Name,
Name: p,
}
if m.dirtyPolicyIDs.Contains(polID) {
return true
}
}
}
return false
}
@@ -975,6 +1080,11 @@ func (m *endpointManager) updateHostEndpoints() { | |||
} | |||
} | |||
|
|||
ifaceNameToPolicyGroups := map[string][]*rules.PolicyGroup{} |
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'm not yet entirely following what this is for. Does it matter that different subsets of these groups will need to be programmed in different iptables tables?
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.
Oh, I see, the groups are going to be unconditionally programmed in all tables. I guess that's OK, albeit a touch inelegant.
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.
Ah yes, I just copied the old inelegant solution for policies. Could do a bit better here because we do know which groups go in which tables; just seemed like the number of tracking maps would explode a bit
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 would personally be fine with leaving this to a future tidy up, as this PR is already a good unit on its own.
}], | ||
} | ||
groups := []*rules.PolicyGroup{group} | ||
for _, name := range names[1:] { |
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.
Is names[1:]
ok if names only has 1 entry?
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, I double checked.
…rojectcalico#8098) Group policies in iptables chains based on the selectors used in the policies. For example, if there are three policies in a row that use the same selector, put them in a group. - Add protobuf field for policy selector pass through from calc graph. - Add policy group calculation to endpoint manager. Does refcounting to manage shared policy group chains. - Add policy group chain rendering to rule renderer. Take advantage of extra indirection to implement pass rules more efficiently (return on pass or accept). - Make iptables reference counting recursive. Instead of refcounting blindly, trigger incref of children only when the chain itself becomes referenced. Trigger decref only for referenced chains. Only invalidate dataplane if changing a referenced chain. CORE-10015
Description
For example, if there are three policies in a row that use the same selector,
put them in a group.
Related issues/PRs
CORE-10015
Todos
Release Note
Reminder for the reviewer
Make sure that this PR has the correct labels and milestone set.
Every PR needs one
docs-*
label.docs-pr-required
: This change requires a change to the documentation that has not been completed yet.docs-completed
: This change has all necessary documentation completed.docs-not-required
: This change has no user-facing impact and requires no docs.Every PR needs one
release-note-*
label.release-note-required
: This PR has user-facing changes. Most PRs should have this label.release-note-not-required
: This PR has no user-facing changes.Other optional labels:
cherry-pick-candidate
: This PR should be cherry-picked to an earlier release. For bug fixes only.needs-operator-pr
: This PR is related to install and requires a corresponding change to the operator.