Skip to content
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

SDN-4604: Networking: egress IP per destination proposal #1594

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
348 changes: 348 additions & 0 deletions enhancements/network/egress-ip-per-destinations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,348 @@
---
title: egress-ip-per-destinations
authors:
- "@martinkennelly"
reviewers:
- "@trozet"
- "@kyrtapz"
approvers:
- "@trozet"
api-approvers:
- "@joelspeed"
creation-date: 2024-03-12
last-updated: 2024-03-12
tracking-link:
- "https://issues.redhat.com/browse/SDN-4454"
see-also:
- "NA"
replaces:
- "NA"
superseded-by:
- "NA"
---

# Egress IP per destination
## Summary
Today, we can use an `EgressIP` to describe the source IP for a pod if it selected by an `EgressIP` custom resource (CR) selectors. This includes namespace
and pod selectors. If multiple `EgressIP` CRs select the same set of pods, the behavior is undefined. This is because
we cannot reliably choose which source IP to use. One of the EgressIPs will be active and the others on stand-by.

This enhancement proposes adding a new selector for when we want to apply the `EgressIP`. This new selector will only apply
the `EgressIP` as the source IP to a set of pods communicating with destination IP if that destination IP is within predefined network CIDRs.

If the new destination traffic selector is specified for all `EgressIP` CRs that selected a set of pods and the destination networks selected do not overlap, then we
can allow a set of pods to have multiple source IPs depending on the destination address.

## Motivation
### User Stories

As a cluster tenant, I want my application traffic to egress a particular interface depending on the traffic destination address and must
have a distinct but consistent source IP for each interface.

As a cluster administrator, I want to easily configure which destination networks should have an `EgressIP` because the destination
networks may change, and I do not wish to have to update individual `EgressIP` custom resources for every update.

### Goals
- Allow a set of pods to have a consistent source IP depending on the destination
- Support shared and local gateway (aka `routingViaHost=true`) modes in-order to reduce asymmetry between the modes
- Backward compatibility with existing `EgressIP` API
- If the new API isn't defined, preserve existing behaviour

### Non-Goals
- Change existing `EgressIP` behavior when the new field is not defined

## Proposal

A new `TrafficSelector` field is added to `EgressIP` spec which uses Kubernetes label selector semantics
to select a new CRD called `EgressIPTraffic` which contains a list of destination networks. This list of networks defines when an `EgressIP` should

Choose a reason for hiding this comment

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

I think it is implied, but to confirm, these aspects of EgressIP do not change as a result of this enhancement (either with or without a traffic selector applied):

  • Traffic egressing under an EIP (either without traffic selector or as a result of being selected) will be load balanced across multiple IP addresses defined within that EIP.
  • Placement of IP addresses from an EIP is based on egress-assignable nodes and interfaces with appropriate subnets to host the IP.

One aspect of this is that the user may design networking connectivity as desired/needed. The multiple egress networks (EIP) do not necessarily need to be connected to the same node(s).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Traffic egressing under an EIP (either without traffic selector or as a result of being selected) will be load balanced across multiple IP addresses defined within that EIP.

yes

Placement of IP addresses from an EIP is based on egress-assignable nodes and interfaces with appropriate subnets to host the IP.

yes

be used as source IP when an application attempts to talk to a destination address, and it is within one of the predefined networks
defined within a `EgressIPTraffic` CR.
Comment on lines +56 to +59

Choose a reason for hiding this comment

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

This allows a user to define the external networks once per network (ie in the EgressIPTraffic CR) which is really nice for maintenance. If a newly created subnet is reachable behind an interface the cluster admin only needs to update the EgressIPTraffic CR which contains the reachable routes for that interface and all EgressIPs making use of it will be updated.
One question...

  • When adding another reachable subnet via an interface, is there a difference in functionality/performance if the user creates an additional EgressIPTraffic CR (with the same label) vs adding an additional CIDR within the existing EgressIPTraffic CR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

nope - no data path performance difference and negligible control plane cpu usage increase.


The reason for not embedding the destination networks within each `EgressIP` CR is that it places a burden
on the cluster administrator to update all `EgressIP` CRs if the destination networks change.

With the Kubernetes label selector semantics, cluster administrators can define a set of destination networks that multiple `EgressIP`
CRs can consume.

### Semantics
#### No `TrafficSelector` selector defined
Existing `EgressIP` behaviour is conserved - any pod selected must have the Egress IP defined as source IP when a packet is egress-ing the node
regardless of the destination IP.

#### `TrafficSelector` is defined but selects zero `EgressIPTraffic` or `EgressIPTraffic` CRs with zero destination networks defined
Any selected pod must have the node IP as source IP when egress-ing the node.

#### Single EgressIP CR selects multiple `EgressIPTraffic` CRs
If greater than one `EgressIPTraffic` CRs are selected, the destination networks are added together, and if a destination IP is within one
of the defined destination networks, then the pods source IP will be the Egress IP.

#### Multiple `EgressIP` CRs selecting a set of pods where one of the `EgressIP` CR defines no `TrafficSelector` selector and one `EgressIP` does
Whichever is the first `EgressIP` CR to become active is going to control the source IP for the pods when traffic is egress-ing the node.
The other EgressIP CRs are in stand-by mode. The order of which `EgressIP` will become active first is undefined.

#### Multiple `EgressIP` CRs selecting a set of pods, where multiple `EgressIP` CRs select one or more EgressIPTraffic and the set of destination networks do not overlap
The source IP of the packet leaving an egress node will be the defined Egress IP if the destination IP is within one
of the selected `EgressIPTraffic` CR destination networks. If it is not within one of the destination networks, a packet
will egress the node the pod resides on with a source IP equal to the nodes IP.

#### Multiple EgressIP CRs select one or more EgressIPTraffic and the set of destination networks overlap partially or fully
The semantics will remain the same as today if multiple `EgressIP` CRs select overlapping set of pods. One of the `EgressIP` CRs will
be active, and the others will be on standby.

Copy link
Contributor

Choose a reason for hiding this comment

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

What if someone specifies Egress IP A with a TrafficSelector and Egress IP B with no traffic selector, and they both match on the same pod. The pod sends traffic that does not match the TrafficSelector, will it still be sent with Egress IP A?

Copy link
Contributor Author

@martinkennelly martinkennelly Mar 25, 2024

Choose a reason for hiding this comment

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

That scenario would be undefined - either EgressIP CR can be "active" but not both because EgressIP B would select all traffic.

Today, if 2 EgressIPs select the same set of pods, one is active and the other is a standby.

With this enhancement, we allow overlapping (i.e. select set of one or more pods) EgressIP CRs only if the selected EgressIPTraffic destination networks do not overlap. If the destination networks overlap, then the behaviour is undefined.

Choose a reason for hiding this comment

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

Combining this question with the previous one (a selector of 0.0.0.0/0). I would anticipate that a common pattern would be a pod with 2 egress networks where one is the "default" and the other hosts specific functionality on a given subnet. In this scenario the user would likely set EIP-A with a specific traffic selector (eg 1.2.3.0/24) and EIB-B with the default 0.0.0.0/0 to catch everything else.

  • Would these be treated as overlapping and fall into the one-active-one-standby scenario?
  • If so, would it be possible to allow at least this narrower use of overlap where one of the EgressIPTraffic contains the default route? Or could one EgressIPTraffic have an isDefault: <true|false> specifier?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. Thanks for this insight from the users perspective.

This would complicate the implementation. We've have to have multiple logical router policy priorities to implement this.

Would these be treated as overlapping and fall into the one-active-one-standby scenario?

Good question and this makes me rethink the existing one-active one standby and instead if there are overlapping networks (like your example), its simply undefined which one gets selected.
Id love to do what you suggested but it increases the complexity of the implementation that I am slow to agree to.

If so, would it be possible to allow at least this narrower use of overlap where one of the EgressIPTraffic contains the default route? Or could one EgressIPTraffic have an isDefault: <true|false> specifier?

We could do this in a future RFE if its a limitation. If we added this, we'd need to add new logical route policies in OVN and new rule priorities in iproute2 ip rules.

Choose a reason for hiding this comment

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

I have a question pending with the customer on whether this aspect is a limitation that can be worked with, or if it is seen as a serious issue. I'll follow up...

Copy link

Choose a reason for hiding this comment

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

Follow up with customer did not raise any concerns.

### Workflow Description

**cluster admin** creates a set of namespaces for tenants to use.
Tenant has requested that its applications need a consistent source IP
for 2 different traffic paths - one health signal traffic and one for work load traffic.
The cluster admin updates a dedicated egress node by attaching two new networks on separate interfaces.
The cluster admin labels this node egress-able.
The cluster admin creates two `EgressIPTraffic` CRs, specifies a destination network in each and labels each CR differently.
The cluster admin creates two `EgressIP` CRs each containing an IP that falls within the previously added networks range.
The two `EgressIP`s selects the same set of pods but different `TrafficSelector`s to select the previously created `EgressIPTraffic` CRs.

Choose a reason for hiding this comment

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

Would it be an invalid configuration if the EgressIP CRs had IP addresses (not traffic selection cidr) which overlap on the same subnet (ie potentially would egress the same interface on the egress-able nodes)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not following - can you elaborate?

Choose a reason for hiding this comment

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

Mostly just a theoretical case...
EIP-a has eip 1.1.1.1 and traffic selector of 10.10.10.0/24
EIP-b has eip 1.1.1.2 and traffic selector of 11.11.11.0/24
Both EIP could be bound to the same interface on an egress node, but traffic going to the 10 net gets SNAT to 1.1.1.1 and traffic going to the 11 net gets SNAT to 1.1.1.2.

The `EgressIP`s are automatically assigned to the secondary host interfaces on the egress node.

The cluster admin gives permissions to the **application admin** to the namespaces.

The **application admin** deploys its application and the application has a consistent but different source IP for the
health signal traffic and the work load traffic.

### API Extensions

Add `TrafficSelector` field to `EgressIPSpec`:
Copy link
Contributor

Choose a reason for hiding this comment

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

Where is this exist (?) type defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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


```golang
// EgressIPSpec is a desired state description of EgressIP.
type EgressIPSpec struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

plan to featuregate the new fields: // +openshift:enable:FeatureGate=Example

Copy link
Contributor Author

@martinkennelly martinkennelly Apr 23, 2024

Choose a reason for hiding this comment

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

Done.
@trozet , will i have to create a downstream only ovn-k patch to add this featuregate restriction to the CRD?
yes, in CNO

// EgressIPs is the list of egress IP addresses requested. Can be IPv4 and/or IPv6.
// This field is mandatory.
EgressIPs []string `json:"egressIPs"`
// TrafficSelector applies the egress IP only to the network traffic defined within the selected EgressIPTraffic(s).
// If not set, all egress traffic is selected.
// +optional
// +openshift:enable:FeatureGate=EgressIPPerDestination
TrafficSelector metav1.LabelSelector `json:"trafficSelector"`
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens when a single traffic selector matches many egressIPSpecs?

What happens when a traffic selector doesn't match any egressIPSpecs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What happens when a single traffic selector matches many egressIPSpecs?

Ill assume you meant matchs many EgressIPTraffic CRs. Ive added an update to the enhancement.

What happens when a traffic selector doesn't match any egressIPSpecs?

Ditto.

// NamespaceSelector applies the egress IP only to the namespace(s) whose label
// matches this definition. This field is mandatory.
NamespaceSelector metav1.LabelSelector `json:"namespaceSelector"`
// PodSelector applies the egress IP only to the pods whose label
// matches this definition. This field is optional, and in case it is not set:
// results in the egress IP being applied to all pods in the namespace(s)
// matched by the NamespaceSelector. In case it is set: is intersected with
// the NamespaceSelector, thus applying the egress IP to the pods
// (in the namespace(s) already matched by the NamespaceSelector) which
// match this pod selector.
// +optional
PodSelector metav1.LabelSelector `json:"podSelector,omitempty"`
}

Choose a reason for hiding this comment

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

Would there be an update to the EgressIP status fields to show what EgressIPTraffic CRs are bound and/or the set of resolved destination CIDR applicable to this EIP?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@trozet WDYT? I am slow to add a new status field for this because it would be optionally filled.

```

New CRD:

```golang
// +genclient
// +genclient:nonNamespaced
// +kubebuilder:object:root=true
// +kubebuilder:subresource:nostatus
// +kubebuilder:resource:shortName=eipt,scope=Cluster
// +kubebuilder:printcolumn:name="DestinationNetworks",type=string,JSONPath=".spec.destinationNetworks"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +openshift:enable:FeatureGate=EgressIPPerDestination
// EgressIPTraffic defines a set of networks outside the cluster network.
type EgressIPTraffic struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added

metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`

Spec EgressIPTrafficSpec `json:"spec"`
}

// EgressIPTrafficSpec defines the desired state description of EgressIPTraffic.
// +kubebuilder:validation:MaxProperties=1
// +kubebuilder:validation:MinProperties=1
type EgressIPTrafficSpec struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

So with this CRD we can specify a list of networks. I expect some customers will want to also match by layer 4 ports. Should we include that in scope for this enhancement?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Id rather leave it out of this enhancement as it wasnt a defined goal - but its up to you, if you feel strongly about it.

// DestinationNetworks is the list of Network CIDRs that defines external destinations
// for IPv4 and/or IPv6.
DestinationNetworks []CIDR `json:"destinationNetworks,omitempty" validate:"omitempty"`
}

// CIDR is a network CIDR. IPv4 or IPv6.
// +kubebuilder:validation:XValidation:rule="isCIDR(self)",message="CIDR must be in valid IPV4 or IPV6 CIDR format"
type CIDR string

// +kubebuilder:object:root=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// EgressIPTrafficList contains a list of EgressIPTraffic
type EgressIPTrafficList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`

Items []EgressIPTraffic `json:"items"`
}
```

### Topology Considerations

#### Hypershift / Hosted Control Planes
Nothing new that's specific for HCP.

#### Standalone Clusters
Nothing specific to standalone.

#### Single-node Deployments or MicroShift
Nothing specific for these deployments.

### Implementation Details/Notes/Constraints

#### Cluster manager
No changes to `EgressIP` node assignment or health checks therefore no changes.

#### OVNKube-controller (OVNKube-node)
If a user defines an `EgressIP` CR and doesn't populate `TrafficSelector` then `EgressIP` behaviour is the same as today (without this proposed feature).
All traffic egress-ing the cluster will be SNAT'd.

If a user defines an `EgressIP` CR and populates the `TrafficSelector` field with a selector, and it matches or does not match an `EgressIPTraffic`
CR but regardless, there are no defined networks among the selected `EgressIPTraffic` CRs, then no cluster external traffic is SNAT'd to the Egress IP. This
matches the behaviour of other "selectors" in `EgressIP`.

##### Common amongst gateway "modes"
If a user defines an `EgressIP` CR and the `TrafficSelector` is populated:-
1. Create an address set managed and consumed specifically by said `EgressIP` CR. This address set will contain all the destination networks
that maybe defined in one or more `EgressIPTraffic` CRs
This implementation detail is necessary because we need to limit the number of address sets to one for any selected set of pod because OVN NAT field `allowed_ext_ips`
allows setting one address set. We cannot set more than one address set to this field.
Also, it limits the inclusion to one extra OVN LRP match condition for the selected pods for the OVN Logical Router `ovn_cluster_router`.
i.e. `ip4.src == $pod_ip && ip4.dst == $address_set_name ..`
martinkennelly marked this conversation as resolved.
Show resolved Hide resolved
2. For each of the selected `EgressIPTraffic` CRs, add, if any, the networks defined to the address set mention in set 1
3. For each selected pod, create an OVN Logical Route Policy entry for OVN Logical Router `ovn_cluster_router` at the
same priority as existing egress IP LRPs, with a match criteria of source IP equalling a selected pod IP and
destination IP equal to the name of the address set defined in step 1 and an action of reroute. The OVN action reroute value
doesn't change with proposal

##### Egress IP assigned to the primary interface
###### Shared gateway / local gateway (aka routingViaHost=true)
Note: Currently, for OCP 4.15, traffic paths for Egress IPs do not alter when an Egress IP is assigned to the primary interface
for shared and local gateway modes - traffic for selected pods always egress via a nodes OVN gateway Logical Router and is not sent to the host
networking stack in local gateway mode.

4. On the egress node, create a new OVN NAT entry and populate the `allowed_external_ips` with the address set name created in step 1
5. On the egress node, add the newly created NAT entry to the egress nodes gateway OVN Logical Router NATs
6. If any selected `EgressIPTraffic` CRs gets created, updated or deleted, then we find any `EgressIP` CRs that select it and ensure
that set of `EgressIP` CRs reconciles

A sync function is required for the aforementioned "destination networks" OVN address sets for the case of any events missed.

##### Egress IP assigned to a host secondary interface
###### Shared gateway
4. On the egress node, for each destination network, create an iproute2 IP rule including `from` pod IP and `to` equaling the network with a lookup
for the custom routing table
5. IPTables configuration remains unchanged

### Risks and Mitigations
Managing a new CRD introduces some risk and this risk would be reduced if the information in that new CRD
is including in the `EgressIP` CRD. i.e. embed the destination networks within the `EgressIP` CRD.
This design choice is taken for user experience and future extensibility.

### Drawbacks
As the routing becomes more complex in the future, with *possibly* more traffic selectors, we probably should use pkt marks and keep the
routing logic exclusively in OVN and only use iproute2 ip rules to act on the marks instead of this proposal which is checking
src and dst. This approach with iproute2 ip rules doesn't make it easy to add more traffic selectors.

Possible premature optimisation by creating a new CRD that's scoped for traffic filtering instead of just for destination address filtering.

## Test Plan
- Unit tests and e2es. E2Es current test framework provide one "external" endpoint. In-order to fully test this feature,
we will need multiple external endpoints
- QE test plan and regression tests

## Graduation Criteria
| OpenShift | Maturity |
|-----------|--------------|
| 4.17 | Tech Preview |
| 4.18 | GA |

### Dev Preview -> Tech Preview
N/A

### Tech Preview -> GA
4.18

### Removing a deprecated feature
- Announce deprecation and support policy of the existing feature
- Deprecate the feature

## Upgrade / Downgrade Strategy
Upgrade expectations:
No disruption to existing `EgressIP` functionality or performance during update

Downgrade expectations:
New field, and we do not expect the user to utilize this feature during an upgrade, therefore, no downgrade issues as the
feature will not be active.

## Version Skew Strategy
N/A

## Operational Aspects of API Extensions
- No status is planned to be included in the new CRD `EgressIPTraffic`
- Expected to very slightly increase CPU/memory usage on every node watch a new CRD and process its changes
- No impact on scalability
- Health can be determined via k8 events / logs / metrics (retry logic failure when configuring LRPs)

## Support Procedures
If you do not see the SNAT to the defined egress IP, then check the network operator status and ensure there are no issues.
Following this, find the node you expect the egress IP to be assigned to by checking the `EgressIP` status.
Then, lookup the OVN Kubernetes pod running on that node and for container `ovnkube-controller`, grep for the `EgressIP`
CR name. If no obvious error, lets confirm OVN Kubernetes created the necessary artifacts in OVN for a
pod selected by the `EgressIP`.

Use `oc` to `rsh` into the OVN Kubernetes pod on that node and ensure there is an OVN Logical Route Policy in Logical Router `ovn_cluster_router`:
```shell
sh-5.2# ovn-nbctl lr-policy-list ovn_cluster_router
...
100 ip{4|6}.src == $POD_IP && ip{4|6}.dst == $ADDRESS_SET_NAME reroute $GR_PORT_IP (primary inf) or $MGNT_PORT_IP (host secondary inf)
...
```
Confirm the network(s) you defined in `EgressIPTraffic` CR(s) and selected by your `EgressIP` traffic selector, are present in the address
set seen previously:
```shell
sh-5.2# ovn-nbctl find address_set name=$ADDRESS_SET_NAME
_uuid : 46b985b9-8869-474a-b17a-2ed36db45cc8
addresses : [$NETWORK_CIDR_1,$NETWORK_CIDR_2,...]
external_ids : {...}
name : $ADDRESS_SET_NAME
```
- If `EgressIP` is assigned to primary interface:
Confirm the Egress IP assigned node OVN gateway Logical Router contains a NAT where the `logical_ip` is the selected $POD_IP:
```shell
sh-5.2# ovn-nbctl lr-nat-list GR_$NODE_NAME
TYPE GATEWAY_PORT EXTERNAL_IP EXTERNAL_PORT LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
...
snat $EGRESS_IP $POD_IP
...
```
Inspect the created NAT to ensure it contains the address set found previously, and it is set to the field `allowed_ext_ips`:
```shell
sh-5.2# ovn-nbctl find NAT logical_ip=$POD_IP
...
allowed_ext_ips : 46b985b9-8869-474a-b17a-2ed36db45cc8
...
type : snat
```
- If `EgressIP` is assigned to a host secondary interface:
Ensure the correct iproute2 IP rules are present for a selected pod. If `EgressIP` used a traffic selector that selects one or
more `EgressIPTraffic` CRs that contain a set of network CIDRs A-Z, then expect a list of IP rules for each network CIDR
```shell
sh-52# ip (-6) rule
...
60000: from $pod_ip to $NETWORK_CIDR_A lookup 4567
...
60000: from $pod_ip to $NETWORK_CIDR_A lookup 4567
...
```

## Alternatives
https://docs.google.com/document/d/1_mhEzjVEEkbMl6k0OKP4rQPxd0eVe7OVuxVmHu29qEA

Drawbacks: Not backward compatible. Change in existing behaviour for routing.

## Infrastructure Needed [optional]
N/A