Page MenuHomeFreeBSD

pf: add SCTP NAT support
ClosedPublic

Authored by kp on Jul 4 2023, 7:12 AM.
Tags
None
Referenced Files
Unknown Object (File)
Mon, May 6, 11:53 PM
Unknown Object (File)
Mon, May 6, 9:31 AM
Unknown Object (File)
Mon, May 6, 9:31 AM
Unknown Object (File)
Mon, May 6, 9:31 AM
Unknown Object (File)
Mon, May 6, 9:31 AM
Unknown Object (File)
Mon, May 6, 9:31 AM
Unknown Object (File)
Mon, May 6, 9:30 AM
Unknown Object (File)
Sat, May 4, 12:29 PM

Details

Summary

Support NAT-ing SCTP connections.

This is mostly similar to UDP and TCP, but we cannot do incremental
checksum updates for SCTP because it's CRC32. We solve that by removing
the 'has SCTP checksum' flag from the mbuf so the checksum is fully
recalculated when the packet is sent out again.

We do use the existing pf_change_ap() function to modify the packet,
because we may still need to update the IPv4 header checksum.

MFC after: 3 weeks
Sponsored by: Orange Business Services

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

kp requested review of this revision.Jul 4 2023, 7:12 AM

Are you changing the port number? Why do you need to recompute the CRC32c?

Are you changing the port number? Why do you need to recompute the CRC32c?

Yes, the NAT code can decide to change port numbers.

Arguably we should only do so if one of the PF_ANEQ() checks is true though (as it is also possible that the port numbers do not change). I'll see if I can do that without repeating too much code.

I'll also update the rdr test to explicitly change a port number, because I've just discovered that my checksum handling isn't quite right.

  • fix checksum recalculation
  • only recalculate if we actually changed anything
In D40866#930487, @kp wrote:

Are you changing the port number? Why do you need to recompute the CRC32c?

Yes, the NAT code can decide to change port numbers.

That is a very bad idea. If NAT functions are on multiple paths are involved, this will break the association. The root cause is that an SCTP endpoint is a pair of a list of addresses and a port number and not a list of pairs of an address and a port number.

Arguably we should only do so if one of the PF_ANEQ() checks is true though (as it is also possible that the port numbers do not change). I'll see if I can do that without repeating too much code.

I'll also update the rdr test to explicitly change a port number, because I've just discovered that my checksum handling isn't quite right.

In D40866#930487, @kp wrote:

Yes, the NAT code can decide to change port numbers.

That is a very bad idea. If NAT functions are on multiple paths are involved, this will break the association. The root cause is that an SCTP endpoint is a pair of a list of addresses and a port number and not a list of pairs of an address and a port number.

Just to make sure I understood your remark correctly: You're talking about a setup where there are multiple active paths and the firewall sees one path, but not all of them. In that case such a configuration would indeed break things.

I have future work planned to teach pf to cope with multihomed connections (and address reconfiguration), but there too things will break if we only see part of the association.

My view here is that this is a limitation we're just going to have to live with. Being able to redirect to different ports (or NAT) and cope with multi homed connections is useful, even if we can only make that work when we see all paths of the association. There are many common scenarios where it will work (e.g. pf running the single border gateway of a network, which means it'll see all traffic), and we do want to enable those setups.

In D40866#930559, @kp wrote:
In D40866#930487, @kp wrote:

Yes, the NAT code can decide to change port numbers.

That is a very bad idea. If NAT functions are on multiple paths are involved, this will break the association. The root cause is that an SCTP endpoint is a pair of a list of addresses and a port number and not a list of pairs of an address and a port number.

Just to make sure I understood your remark correctly: You're talking about a setup where there are multiple active paths and the firewall sees one path, but not all of them. In that case such a configuration would indeed break things.

Yes, this is what I'm referring to. If you would put a middlebox on all paths, it becomes a single point of failure. Something you might want to avoid, when you are using SCTP with multihoming.

I have future work planned to teach pf to cope with multihomed connections (and address reconfiguration), but there too things will break if we only see part of the association.

Well, it depends on how the end-points behave.
A client could send packets containing an INIT chunk to all known addresses of the peer (using the sctp_connectx() call). Then you would observe the INIT/INIT-ACK exchange on all paths and could establish state. The full handshake is only seen on one. An alternate way is to setup the association from the client side with a single address, but the server uses multiple. Then the clients can use address reconfiguration to add other addresses. So the alternate middleboxes would observe an ASCONF/ASCONF-ACK exchange.
However, any middlebox changing the port number in one of the middleboxes will break this.

One could use the verification tag to handle port number collisions as described in draft-ietf-tsvwg-natsupp-23, however that document was killed by the ADs after WGLC.

My view here is that this is a limitation we're just going to have to live with. Being able to redirect to different ports (or NAT) and cope with multi homed connections is useful, even if we can only make that work when we see all paths of the association. There are many common scenarios where it will work (e.g. pf running the single border gateway of a network, which means it'll see all traffic), and we do want to enable those setups.

It is up to you what you implement. I tried to get an SCTP-specific solution out and failed...

General question: Why do you want to change an SCTP port number at all?

General question: Why do you want to change an SCTP port number at all?

Mostly because that's how everything (Well, TCP and UDP, but that's everything, right?) else works too.

Also because disallowing that would be more complicated than allowing it, both because of user expectations and because it'd be different from how we handle TCP and UDP.

D40868 has a trivial example. The basic idea is to simulate a border gateway (think home user router, basically) that's doing NAT and has a service on the LAN that it wants to allow WAN access to. That's redirected/rewritten with the 'rdr' rule, and that commonly also means a port rewrite.

It's not impossible to add extra validation in pfctl and the kernel ioctl layer to actively prohibit this, but there are a number of scenarios where it will just work and it seems like a bad idea to disallow those because there are setups where it won't work. Firewalls are generally giant foot guns already (e.g. we let users drop ICMPv6 traffic, breaking PMTU), so adding one more way users can remove their own appendages doesn't really worry me.

In D40866#931084, @kp wrote:

General question: Why do you want to change an SCTP port number at all?

Mostly because that's how everything (Well, TCP and UDP, but that's everything, right?) else works too.

This is the point. SCTP is different. Doing what is appropriate for TCP and UDP, might not be appropriate for SCTP. Changing the port number for no reason.

Also because disallowing that would be more complicated than allowing it, both because of user expectations and because it'd be different from how we handle TCP and UDP.

Why do you expect this for TCP or UDP. I do understand that you want to do it in case of a local port number collision, but that is sort of an exception.

D40868 has a trivial example. The basic idea is to simulate a border gateway (think home user router, basically) that's doing NAT and has a service on the LAN that it wants to allow WAN access to. That's redirected/rewritten with the 'rdr' rule, and that commonly also means a port rewrite.

Again: this breaks multihoming, which is used for redundancy.

Do you have a use case in mind where people are using SCTP?

It's not impossible to add extra validation in pfctl and the kernel ioctl layer to actively prohibit this, but there are a number of scenarios where it will just work and it seems like a bad idea to disallow those because there are setups where it won't work. Firewalls are generally giant foot guns already (e.g. we let users drop ICMPv6 traffic, breaking PMTU), so adding one more way users can remove their own appendages doesn't really worry me.

I understand that. It just worries me, since people can not use SCTP in a multihomed scenario if NATs rewrite port numbers without good reasons. But again: I tried to specify how NATs should behave in a cooperative way with end-points and failed to get it to an RFC.

In D40866#931084, @kp wrote:

General question: Why do you want to change an SCTP port number at all?

Mostly because that's how everything (Well, TCP and UDP, but that's everything, right?) else works too.

This is the point. SCTP is different. Doing what is appropriate for TCP and UDP, might not be appropriate for SCTP. Changing the port number for no reason.

Also because disallowing that would be more complicated than allowing it, both because of user expectations and because it'd be different from how we handle TCP and UDP.

Why do you expect this for TCP or UDP. I do understand that you want to do it in case of a local port number collision, but that is sort of an exception.

Right. I'd expect the NAT code to mostly maintain the existing port numbers, but users may decide to configure that differently (e.g. it's possible to set the port numbers that NAT is allowed to map to), and as you say, there might be port number collisions where we need it anyway.

D40868 has a trivial example. The basic idea is to simulate a border gateway (think home user router, basically) that's doing NAT and has a service on the LAN that it wants to allow WAN access to. That's redirected/rewritten with the 'rdr' rule, and that commonly also means a port rewrite.

Again: this breaks multihoming, which is used for redundancy.

Wouldn't NAT break multihoming (where we don't see the the full connection) anyway? Just changing the IP addresses is pretty inescapable for the typical home user NAT setup for example. That's the happy case where we're pretty much guaranteed to see the entire connection (so between all involved endpoints), which we can make work, changed addresses and ports or no, but if we NAT one path even without changing the ports and not another I'd expect that to break too.

Do you have a use case in mind where people are using SCTP?

Orange have a use for it, but I don't know any details of their specific setup. We did briefly discuss the issues with multihoming, and for their use case we expect to see all paths between the endpoints.

It's not impossible to add extra validation in pfctl and the kernel ioctl layer to actively prohibit this, but there are a number of scenarios where it will just work and it seems like a bad idea to disallow those because there are setups where it won't work. Firewalls are generally giant foot guns already (e.g. we let users drop ICMPv6 traffic, breaking PMTU), so adding one more way users can remove their own appendages doesn't really worry me.

I understand that. It just worries me, since people can not use SCTP in a multihomed scenario if NATs rewrite port numbers without good reasons. But again: I tried to specify how NATs should behave in a cooperative way with end-points and failed to get it to an RFC.

Looking at the code now, I'm rather less confident about denying port changes for SCTP. The whole infrastructure in pf doesn't really distinguish the rdr from the nat case.

In D40866#934112, @kp wrote:
In D40866#931084, @kp wrote:

General question: Why do you want to change an SCTP port number at all?

Mostly because that's how everything (Well, TCP and UDP, but that's everything, right?) else works too.

This is the point. SCTP is different. Doing what is appropriate for TCP and UDP, might not be appropriate for SCTP. Changing the port number for no reason.

Also because disallowing that would be more complicated than allowing it, both because of user expectations and because it'd be different from how we handle TCP and UDP.

Why do you expect this for TCP or UDP. I do understand that you want to do it in case of a local port number collision, but that is sort of an exception.

Right. I'd expect the NAT code to mostly maintain the existing port numbers, but users may decide to configure that differently (e.g. it's possible to set the port numbers that NAT is allowed to map to), and as you say, there might be port number collisions where we need it anyway.

The last case was what we addressed in draft-ietf-tsvwg-natsupp.

D40868 has a trivial example. The basic idea is to simulate a border gateway (think home user router, basically) that's doing NAT and has a service on the LAN that it wants to allow WAN access to. That's redirected/rewritten with the 'rdr' rule, and that commonly also means a port rewrite.

Again: this breaks multihoming, which is used for redundancy.

Wouldn't NAT break multihoming (where we don't see the the full connection) anyway? Just changing the IP addresses is pretty inescapable for the typical home user NAT setup for example. That's the happy case where we're pretty much guaranteed to see the entire connection (so between all involved endpoints), which we can make work, changed addresses and ports or no, but if we NAT one path even without changing the ports and not another I'd expect that to break too.

No, you can do NAT in a multihoming environment as long as the port numbers are not changed in an inconsistent way. Assuming the client side is behind NATs, the crucial point is that the client side does not put any addresses insides the INIT chunk and that the NATs don't change port numbers in a non consistent way. The idea is that the NATs don't change the port number at all, the client will use the ASCONF game to add additional addresses. NATs will also add an entry in its NAT binding table when seeing the ASCONF/ASCONF-ACK exchange. Please note that the client will use a wildcard address in the ASCONF chunk, since the NAT can't change it. Adding the wildcard address just just means add the source address of the packet. This is not protected by the AUTH mechanism and can be changed by the NATs. See Multihomed Client and Server for an example.

Do you have a use case in mind where people are using SCTP?

Orange have a use for it, but I don't know any details of their specific setup. We did briefly discuss the issues with multihoming, and for their use case we expect to see all paths between the endpoints.

That means the the NAT is a single point of failure. So why are they using multihoming?

It's not impossible to add extra validation in pfctl and the kernel ioctl layer to actively prohibit this, but there are a number of scenarios where it will just work and it seems like a bad idea to disallow those because there are setups where it won't work. Firewalls are generally giant foot guns already (e.g. we let users drop ICMPv6 traffic, breaking PMTU), so adding one more way users can remove their own appendages doesn't really worry me.

I understand that. It just worries me, since people can not use SCTP in a multihomed scenario if NATs rewrite port numbers without good reasons. But again: I tried to specify how NATs should behave in a cooperative way with end-points and failed to get it to an RFC.

Looking at the code now, I'm rather less confident about denying port changes for SCTP. The whole infrastructure in pf doesn't really distinguish the rdr from the nat case.

Sorry for the delay, I was distracted by a couple of other issues.

Looking at the nat code again, it's less impactful to teach it not to re-map SCTP ports, so I've made that change. The userspace bits still allow configuration of port changes, but the kernel won't actually do it.

I'd prefer to be able to warn or refuse that in userspace too, but I think I can live without that for now.

This revision is now accepted and ready to land.Jul 20 2023, 5:36 PM
This revision was automatically updated to reflect the committed changes.