Page MenuHomeFreeBSD

libalias: Allow setting alias port ranges

Authored by nc on Feb 1 2020, 3:31 AM.



Allow setting alias port ranges in libalias and ipfw. Along with r357092, this will allow a system to be a true RFC 6598 NAT444 setup, where each network segment (e.g. user, subnet) can have their own dedicated port aliasing ranges.

Submitted by: Neel Chauhan <neel AT neelc DOT org>

Test Plan

In short: test a network with an internal IP address in the with port_alias LOWER UPPER in IPFW, and see if NAT is performed.


Compile a HEAD with this patch and reboot.

Add the following to /etc/rc.conf:

ifconfig_lan0="inet netmask"

Add the following to /etc/ipfw.conf:


ipfw -q flush

ipfw nat 1 config if wan0 unreg_cgn port_alias 2000-3000
ipfw add 100 nat 1 ip from any to me 2000-3000 in via wan0
ipfw add 200 nat 1 ip from to any out via wan0
ipfw add allow ip from any to any

Replace 2000 and 3000 with your lower and upper port ranges. Keep in mind that both have to be greater than 1024, and UPPER (obviously) has to be greater than LOWER.

Replace wan0 with your WAN (outside) interface, and lan0 with your LAN (inside) interface.

Then run

kldload ipfw ipfw_nat


service netif restart

Then, add clients on the subnet with the gateway and subnet mask.

You could also do DHCP, or NAT from a loopback interface, I won't mention that here.

Diff Detail

rG FreeBSD src repository
Lint Not Applicable
Tests Not Applicable

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
nc marked 2 inline comments as done.Feb 7 2020, 4:14 PM
nc added inline comments.

Sure, fixed it.


Fixed it.


I'm not sure if this would work, but here it is.

nc marked 2 inline comments as done.Feb 7 2020, 4:14 PM

Line 187 and 188 have exactly the same match, so only 187 is invoked. 188 will never be used. Furthermore they are only outgoing (for aliasing), they do not match incoming packets (for dealiasing)

In the test, incoming connections are now based on their port, each NAT host gets their own port range.

Somewhat unrelated (unrelated?), I believe we should commit D23448 before this.


I'd prefer a function witch return the valid port number or 0 in the case of failure. Writable pointers in arguments open a special class of worms in debugging.


Did you try "12345six" as a port number?


Did try "7654321" as a port number?


Is this case already covered by the validation functions? If yes, remove those check.


Using a other type of function call this would be

(0 < (hp = nat_port_...(av[1])))

which is easier to audit.


So PKT_ALIAS_SAME_PORT is incompatible with port ranges?
Can we prevent setting both flags with an error message in parsing the config?


How about a stuct and a single enty in the record?

struct nat_port_range {
  u_short lower, upper;


struct nat_port_range portRange;

Here the struct can be reused.


Using a stuct the lines become

ptr->portRange = ucfg->portRange;
In D23450#516086, wrote:
In D23450#515858, wrote:

Also, may I please have your patch for NAT table lookup? It would work very well for this.

Yep, I'v to adapt it to CURRECT, the structure of accessing the "nat" component in ipfw was changed (which make it much easier to apply a different access scheme)

Please have a look at D23586

I made the requested changes.

UPDATE: I forgot the PKT_ALIAS_SAME_PORT change, I realized that after I posted this so I am working on that now.

Also Lutz, thank you so much for posting D23586.

nc marked 3 inline comments as done.

Made the change.

nc marked 7 inline comments as done.Feb 8 2020, 11:58 PM
nc marked an inline comment as done.

You do not have to follow my comments.
I just express my feelings, which may be wrong or misleading.
If your idea is different, please feel free to refuse the advice.


The common idiom is to provide a pointer as the second argument ti strtol and inspect the (first unparsable) char it points to afterwards.

port = strtol(ptr, ptr2, 10);
if ( *ptr2 != '\0' || port < 1024 )

u_short comparsion with MAX_USHORT is not recommended.


Given the fact, that the upper limit is one above the usable range, this can't be specified, because the port number to configure is out of range. So it might be advisable to redefine the upper limit to be part of the interval (2000 2999 instead of 2000 3000).


Why not take the (pointer to the) struct as argument?

I'm going to revert to the code to the one without the struct nat_port_range since it caused more problems than it's worth, especially with #include statements.

Your other requested changes, including those to the port parsing and options checking will be merged in the new patch.

Here's my updated patch.


The pointer end will always be != NULL, so you may even allow port numbers below 1024.


No no. The concern is only valid as long as the documentation about the half open interval was missing. The configuration you need (using half open intervals) is 2000 3000 and 3000 4000 in order to match the port ranges in lines 187-188 below.

(Hopefully) fixed the interval and port parsing.

jilles added inline comments.

It looks like that will still get through, since the cast will convert it to 52145 (the C standard defines this conversion, although it is not what is wanted here).

I suggest storing the result from strtol() in a variable of type long and doing range checks on that.

I wonder how it is possible to configure the whole range 65000 to 65535 as usable ports,


long is indeed better than any unsigned type, because it would catch negative numbers easily.

Using long for the port parsing sounds good. Port numbers obviously can't be negative, neither than overflowed ints.

New patch does exactly this.

Can you please mark all the comments as "Done", which are solved. Only the author of the patch can do this.

nc marked 8 inline comments as done.Feb 10 2020, 3:02 PM

Sure, done that.

I'm still not satisfied with the "upper bound", which is inconsistent between "config port range" and "matching port range" in the ipfw rule set. It does not allow to specify the highest port (but this is a minor issue).

Furthermore I'm not familiar with the test framework, so somebody else should have a look at this part. Especially, because the is no success report here (but a failure). I don't want to stumble over an erroneous test ...

So most of my concerns are handled, I can use this patch to extend it to an dynamic port allocator (for less extensive lawful logging).

But I'm not in the position to give the final go.

Thanks for your feedback.

I'm thinking about switching the NAT port range to something like 2000-2999 instead of 2000 3000 for consistency with the rest of IPFW. Would this be okay?

In D23450#518558, wrote:

Thanks for your feedback.

I'm thinking about switching the NAT port range to something like 2000-2999 instead of 2000 3000 for consistency with the rest of IPFW. Would this be okay?

I'd feel better with this, yes. It solves some corner cases, like storing the upper bound in ushort.


Given we're defining port range here, wouldn't the port_range be a bit more relevant name here?


Why do we need to check for both lower and upper in fast path?

Here, I switch to the range separated by a - (e.g. 2000-2999 instead of 2000 3000), where the upper number is also included. On lines 611 and 668 in alias_db.c I added a " + 1" in order to account for the new range allocation mechanism.

I also switched the argument to port_range from port_alias.

In general, I'm pleased with the renaming from the generic "alias" to "range".


That's not true any longer. The interval is closed now, it includes the end points.


Why parsing from again from the very beginning of the string?


In terms of performance, it would be interesting to replace "Upper" with a precomputed "Range" or "Length". This part is used for ever new flow (very often).

For printing the configuration, the "Upper" value an be synthesized on demand.

Minor man page change. You can run checks on man pages with "mandoc -Tlint" and textproc/igor.


You also need to have a line break after a sentence stop here.

Here, I made changes to the:

  • man page
  • argument parsing
  • switch to a range, per Lutz's suggestion
nc marked 5 inline comments as done.Feb 13 2020, 4:44 PM
This revision is now accepted and ready to land.Feb 26 2020, 5:36 PM
gbe added a subscriber: gbe.


Couple trivial cleanups and then I think this is good.


There's some extraneous whitespace here.


and here.


'You need' (missing 'e'), and line length.


Line length, and small inconsistency in that some errors have their first word capitalised and some don't.


Line length.

Here is an updated version with some of the fixed typos/formatting.

This revision now requires review to proceed.Feb 2 2021, 4:53 AM
nc marked 6 inline comments as done.Feb 2 2021, 4:54 AM
0mp added a subscriber: 0mp.

I approve as a mentor assuming that you get an approval from a src committer, @nc

This revision is now accepted and ready to land.Feb 2 2021, 3:06 PM

Approved by: kp

Lutz and Neel will have to work out who actually commits it :)

Congrats @nc for the src bit.
Your turn!