Skip to content

Fix Vxlan code + small additions #509

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

Merged
merged 13 commits into from
May 23, 2025
Merged

Fix Vxlan code + small additions #509

merged 13 commits into from
May 23, 2025

Conversation

Fredi-raspall
Copy link
Contributor

No description provided.

@Fredi-raspall Fredi-raspall requested a review from a team as a code owner May 21, 2025 20:49
@Fredi-raspall Fredi-raspall added the dont-merge Do not merge this Pull Request label May 21, 2025
@Fredi-raspall
Copy link
Contributor Author

Marking as dont-merge until I double check that these are the good fixes and @daniel-noland approves.

@Fredi-raspall Fredi-raspall force-pushed the pr/fredi/recovery_vxlan branch from 2ee184c to c3a73b2 Compare May 21, 2025 20:59
Copy link
Member

@qmonnet qmonnet left a comment

Choose a reason for hiding this comment

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

Changes look OK to me

@Fredi-raspall Fredi-raspall force-pushed the pr/fredi/recovery_vxlan branch 3 times, most recently from 6686ec8 to 659328e Compare May 22, 2025 18:32
@@ -319,6 +319,11 @@ impl Headers {
Headers::default()
}

/// Add / Replace Ethernet header
pub fn set_eth(&mut self, eth: Eth) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

technically this method is unsafe in the sense that you can set an ethernet header with an ethertype which doesn't match the lower layers of the frame. Will think about a good way to fix this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Having thought about it some more, I don't think we can fix this safety issue without requiring the user to go through a strongly typed builder API on every packet manipulation.

The actual fix here should be to make the API contract of the packet / headers types free form and then create a validated type wrapper which does not allow mutations without consuming the structure back into the mutable form.

That will prevent us from sending (or accepting) total nonsense while still giving us the latitude to actually manipulate packets.

That said, wherever possible, we should still forbid creating nonsense packets; moving errors closer to their actual cause point is just good practice anyway.

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 would say it's the caller's responsibility to set the ether-type. I mean, IMO, the net crate should provide the machinery to build packets, but can't be foreseeing all possible combinations and, if it ought to, it should probably be after the fact maybe when deparsing(), or via some validation method to be called explicitly?

/// This method _will not_ update the checksum of the header.
/// # Errors
/// This method returns [`Ipv4LengthError`] if the value is too big
pub fn set_payload_len(&mut self, payload_len: u16) -> Result<(), Ipv4LengthError> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

again, technically an unsafe method. Invariants pushed to the caller.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is probably a distinct case. Here consistency matters a bit more. There's distinct ways we could enforce correctness. Either with validation or by "encapsulation"; i.e. somewhat emulate a stack by wrapping layers. But this requires a significant refactor and APIs? E.g. you could have something like packet.eth_encap( Params ), with packet being anything above L2 (ARP, IPv4, IPv6). This would get the packet, automatically determine ethertype and use params to set src/dst macs. Similarly you could have something like packet.ip_encap( Params ) (where packet would be assumed to contain L4 stuff maybe) and the params contain the fields that would make sense for the user to set. Protocol, header-length, packet length, and so on could be set automatically. So you could do things like:

udp_packet.ip_encap( l3-params ).eth_encap( l2-params )

@@ -74,6 +74,11 @@ impl<Buf: PacketBufferMut> Packet<Buf> {
&self.payload
}

/// Add / Replace Ethernet header
pub fn set_eth(&mut self, eth: Eth) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

technically unsafe

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How do you want to proceed? You want me to annotate all those methods as unsafe?

@@ -133,10 +138,7 @@ impl<Buf: PacketBufferMut> Packet<Buf> {
/// ```
pub fn vxlan_decap(&mut self) -> Option<Result<Vxlan, ParseError<EthError>>> {
match self.headers.try_vxlan() {
None => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why are we removing the log here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We call vxlan_decap for every packet destined to the gateway, but the gateway may receive packets that are not vxlan (control-plane, icmp, etc..). So the prior log would show for every packet sent to it.


/// Wraps a packet in an `Option` depending on the metadata:
/// If [`Packet`] is to be dropped, returns `None`. Else, `Some`.
pub fn enforce(self) -> Option<Self> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I follow the logic for the naming here. Why enforce?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When we iterate over a burst of packets, we pervasively use filter_map which requires returning Some(packet) or None to indicate if the packet should be returned or not. The routing stages (as currently defined) annotate in the packet metadata what to do next: 1) no annotation, 2) some drop condition (there are plenty) 3) whether the packet can be delivered etc. These annotations happen inside the pipeline functions. The enforce is just some sugar syntax to, based on those, determine if Some(packet) / None is to be returned. So, most stages end up calling packet.enforce(). This allows hiding all that logic to the stages and allowing to change it without needing to change them. That's the explanation. Given this purpose, do you have suggestions for the naming? You're the native speaker here!

Copy link
Collaborator

@daniel-noland daniel-noland left a comment

Choose a reason for hiding this comment

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

I think we need to consider some API issues here, but my only actual hard concern is the naming of the enforce method. Given that this is easily refactored later, I'm ok with approving this.

That said, we should talk through my thoughts on a validated packet type at some point

@Fredi-raspall
Copy link
Contributor Author

Fredi-raspall commented May 22, 2025

I think we need to consider some API issues here, but my only actual hard concern is the naming of the enforce method. Given that this is easily refactored later, I'm ok with approving this.

That said, we should talk through my thoughts on a validated packet type at some point

I can easily change the naming for enforce if we find a better name. Not that I like it either....
How about:

  • conclude()
  • decide()
  • resolve()
  • determine()

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Either Daniel forgot these in the final PR, or we developed it
on the phone 😂

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
There are several changes in this function.
The only one that is surely needed is the overwrite of headers
with the cloned-then-modified headers at the end of the method.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Malformed => used when the packet does not seem to be right.
MissingEtherType => used when we can't determine ethertype to use

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
The alternative is to log from the caller if needed.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
@Fredi-raspall Fredi-raspall force-pushed the pr/fredi/recovery_vxlan branch from 43fd427 to caaf7bf Compare May 23, 2025 11:46
@mvachhar mvachhar removed the dont-merge Do not merge this Pull Request label May 23, 2025
@mvachhar mvachhar added this pull request to the merge queue May 23, 2025
@mvachhar mvachhar removed this pull request from the merge queue due to a manual request May 23, 2025
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Adds a method to compute a hash over invariant packet fields,
including ethernet header fields and vlan plus the fields consi-
dered already for ecmp hashing.

A second method leverages the aforementioned to build an entropy
source UDP port suitable for VxLAN encapsulations.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
To encapsulate a packet in VxLAN, a UDP header needs to be built.
In the current API, the caller is requested to build and pass the
UDP information. All of the information in the UDP header, however,
can be automatically be set by the API. This includes:
  * dst port, which should almost always be the well-known value.
  * source port, which can be computed from the payload itself.
  * length
  * checksums (optional for ipv4)

This patch changes the API to not require UDP header and build it
automatically, so that callers need not have to set those.
This may be useful when needing to encapsulate traffic e.g. for
L2 EVIs.

Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
Signed-off-by: Fredi Raspall <fredi@githedgehog.com>
@mvachhar mvachhar enabled auto-merge May 23, 2025 16:18
@mvachhar mvachhar added this pull request to the merge queue May 23, 2025
Merged via the queue into main with commit a0f844e May 23, 2025
14 checks passed
@mvachhar mvachhar deleted the pr/fredi/recovery_vxlan branch May 23, 2025 16:28
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.

4 participants