Skip to content

listener: filter chain selection based on destination IP/port.#3851

Merged
mattklein123 merged 10 commits intoenvoyproxy:masterfrom
PiotrSikora:filter_chain_destination
Jul 13, 2018
Merged

listener: filter chain selection based on destination IP/port.#3851
mattklein123 merged 10 commits intoenvoyproxy:masterfrom
PiotrSikora:filter_chain_destination

Conversation

@PiotrSikora
Copy link
Contributor

Risk Level: Medium
Testing: bazel test //test/...
Docs Changes: Minimal
Release Notes: Added

Signed-off-by: Piotr Sikora piotrsikora@google.com

*Risk Level*: Medium
*Testing*: bazel test //test/...
*Docs Changes*: Minimal
*Release Notes*: Added

Signed-off-by: Piotr Sikora <piotrsikora@google.com>
@PiotrSikora
Copy link
Contributor Author

cc @ggreenway @rshriram

}

void ListenerImpl::addFilterChainForDestinationPorts(
DestinationPortsMap& destination_ports_map, uint16_t destination_port,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filter_chains_ is converted to destination_ports_map ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not converted, filter_chains_ is of the DestinationPortsMap type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to rename it for clarity? :).

filter_chain_match.application_protocols().end());

addFilterChain(server_names, filter_chain_match.transport_protocol(), application_protocols,
addFilterChain(filter_chain_match.destination_port().value(), destination_ips, server_names,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if destination_port is missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's 0 by default.

Omitting those fields in configuration didn't affect existing tests, and we have pretty good coverage there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other places in the codebase use PROTOBUF_GET_WRAPPED_OR_DEFAULT() for this behavior. I think this is a special case because the default for this type is 0, but it feels weird to me to get thing() when has_thing()== false.

Copy link
Member

@rshriram rshriram left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Thanks for the PR.

Copy link
Member

@ggreenway ggreenway left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great overall. A few minor nits.

Also, I didn't see a test for clients connecting via UDS; please add one (or point me to it if I missed it).

std::vector<std::pair<ServerNamesMapSharedPtr, std::vector<Network::Address::CidrRange>>> list;
for (const auto& entry : destination_ips_map) {
std::vector<Network::Address::CidrRange> subnets;
if (entry.first == EMPTY_STRING) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entry.first.empty() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, although I feel that the old check was a bit more correct, since we're checking if it's the map[EMPTY_STRING] entry.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, right, I forgot that's what it is doing. In that case, I'd be ok with putting it back to how you had it.

}
}
destination_ips_pair.second.reset(
new Network::LcTrie::LcTrie<ServerNamesMapSharedPtr>(list, true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::make_unique?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


// Use invalid IP address (matching only filter chains without IP requirements) for UDS.
if (address->type() != Network::Address::Type::Ip) {
address = Network::Utility::parseInternetAddress("255.255.255.255");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we create a static object to use for this, instead of parsing and allocating for each connection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

filter_chains_;
// Mapping of FilterChain's configured destination ports, IPs, server names, transport protocols
// and application protocols, using structures defined above.
DestinationPortsMap filter_chains_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename to destination_ports_map_? I both like the current name, and dislike it. From a high level, filter_chains_ feels like the right name, but when reading the functions that use it, it is hard to follow. So, I'm not sure what I'm suggesting with this comment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I commented above, the code would read better if we simply called this destination_ports_map (in the filter chain match section, which is the crux of this entire thing)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

filter_chain_match.application_protocols().end());

addFilterChain(server_names, filter_chain_match.transport_protocol(), application_protocols,
addFilterChain(filter_chain_match.destination_port().value(), destination_ips, server_names,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other places in the codebase use PROTOBUF_GET_WRAPPED_OR_DEFAULT() for this behavior. I think this is a special case because the default for this type is 0, but it feels weird to me to get thing() when has_thing()== false.

}
}

void ListenerImpl::finishFilterChain() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function needs a more descriptive name. Maybe convert the comment above into the name:

// Convert DestinationIPsMap to DestinationIPsTrie for faster lookups.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to convertDestinationIPsMapToTrie().

I originally called it finishFilterChain(), since we'll need to convert more stuff in the future, if we're going to use LcTrie for source IPs, etc., but I guess we can rename it when needed.

@ggreenway ggreenway self-assigned this Jul 12, 2018
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
@PiotrSikora
Copy link
Contributor Author

@ggreenway all nits addressed. I'll add test for UDS shortly (there isn't explicit one, but since findFilterChain() is exercised for all requests, other tests that were using UDS were failing because of no filter chain match).

This reverts commit 4cd5313.

Signed-off-by: Piotr Sikora <piotrsikora@google.com>
@PiotrSikora PiotrSikora force-pushed the filter_chain_destination branch from 567bb68 to 019a602 Compare July 12, 2018 22:59
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
@PiotrSikora
Copy link
Contributor Author

@ggreenway tests for UDS clients added, they all fail (well, Envoy is crashing when calling LcTrie::getData()) if I comment out the fake address workaround.

Copy link
Member

@ggreenway ggreenway left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great. Thanks!

Edit: What do you mean by "fake address workaround"?

@PiotrSikora
Copy link
Contributor Author

PiotrSikora commented Jul 13, 2018

@ggreenway by "fake address workaround" I mean using 255.255.255.255 as the destination IP when client connected using UDS.

@mattklein123
Copy link
Member

@PiotrSikora can you merge master to see if it fixes the flakes?

@PiotrSikora
Copy link
Contributor Author

@mattklein123 yay, flakes fixed!

@mattklein123 mattklein123 merged commit 01d2e16 into envoyproxy:master Jul 13, 2018
@PiotrSikora
Copy link
Contributor Author

PiotrSikora commented Jul 18, 2018

I'm still digging into this, but it seems that the overhead of LcTrie (for 0.0.0.0/0 and 0::/0) is ~16MB, which is the price we pay for each configured listener / filter chain.

I'd rather not revert this right away (but it's understandable if that's going to happen) and try to figure out whether:
a) I'm misusing LcTrie,
b) there is an obvious an bug in LcTrie,
c) we can migrate lookups to the "dumb" IP list, without reverting the whole change.

cc @rshriram @mattklein123 @ggreenway @ccaraman @brian-pane @htuch

@brian-pane
Copy link
Contributor

If I remember correctly, there's still a step in the LcTrie construction code that pre-allocates a very large buffer to hold nodes (because it doesn't know the exact node count until it has finished constructing and level-compressing the trie) and then tries to realloc it down to just the needed size. That's a holdover from the reference implementation in the original LC Trie paper, which wasn't what I'd consider production-quality code.

If that's the problem, it's definitely fixable. I can set aside some time to work on it this weekend if nobody else gets to it first.

@PiotrSikora
Copy link
Contributor Author

@brian-pane yeah, the leak appears to originate in LcTrieInternal::build(), perhaps realloc never frees the memory?

@PiotrSikora
Copy link
Contributor Author

PiotrSikora commented Jul 18, 2018

@brian-pane shrink_to_fit() is a one line fix for that (see: #3890), thanks for pointing me in the right direction!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants