Page MenuHomeVyOS Platform

Dynamic ipv4 interface list hairpin
Open, WishlistPublicFEATURE REQUEST

Description

Ability to have a list (ipset) of dynamic IP addresses assigned via DHCP/PPPoE etc.
With this list, we will be able to improve the ability of flexible configurations.

It will be useful for firewall/PBR/NAT/port_forwarding features.

As one example:

nat-example.png (546×580 px, 42 KB)

Router main receives a DHCP address on eth4 from ISP.
This address is associated with the DynDNS site record.
Clients from the "internal network" should go to the external address/port 80 of the main router and redirect to the site located on the service device (192.168.68.101).

vyos@main# set nat destination rule 200 destination 
Possible completions:
   address      Destination IP address, subnet, or range
   port         Destination port

We can't use the IP address because it's a dynamic address that we don't know in advance.
We can't use only port 80 because all packets will be forwarded.

Details

Difficulty level
Unknown (require assessment)
Version
-
Why the issue appeared?
Will be filled on close
Is it a breaking change?
Unspecified (possibly destroys the router)
Issue type
Feature (new functionality)

Event Timeline

Unknown Object (User) added a subscriber: Unknown Object (User).Apr 1 2020, 4:04 PM

One question, I don't understand why we can't use only port 80 without this dynamic WAN IP address. In any case, you have inbound interface and port, I think this will be enough.

@Dmitry

set nat destination rule 102 destination port '80'
set nat destination rule 102 inbound-interface 'eth2'
set nat destination rule 102 protocol 'tcp'
set nat destination rule 102 translation address '192.168.68.101'
set nat destination rule 102 translation port '80'

How will internal clients gain access to external sites if we forward all packets with dst port 80?
This is just one example.

Unknown Object (User) added a comment.Apr 1 2020, 4:36 PM

Ok, as a workaround you can you.

set nat destination rule 102 source address !192.168.68.0/24
Unknown Object (User) added a subscriber: Unknown Object (User).Aug 17 2020, 2:48 PM
erkin renamed this task from Dynamic ipv4 interface list. to Dynamic ipv4 interface list.Aug 30 2021, 7:48 AM
erkin set Issue type to Feature (new functionality).
erkin removed a subscriber: Active contributors.

This feature would be very helpfull for hairpin nat as we can see from the mentions.
Might also be helpfull for ipv6 as well.
I am aware its a different product but edgeos from ubiquiti does something like this (looks to be a managed address group that populates dynamically) for nat and fw:

destination {
    group {
        address-group ADDRv4_eth0
    }
}
aderouineau added a subscriber: aderouineau.

Any update on this, since it's been more than 2 years since the initial request? This would indeed be very useful for hairpin NAT. It it complicated to implement?

I just want to really strongly second this issue — if this feature isn't added and I can't find a good workaround, I won't be able to stick with VyOS :(

This feature would be very helpfull for hairpin nat as we can see from the mentions.
Might also be helpfull for ipv6 as well.
I am aware its a different product but edgeos from ubiquiti does something like this (looks to be a managed address group that populates dynamically) for nat and fw:

destination {
    group {
        address-group ADDRv4_eth0
    }
}

This seems like the simplest and best way to do it. I was sort of expecting/hoping for something like this.

I want to see if I can come up with a vbash script hack to set an address group to contain the wan IP — maybe it can be done on a cron job?

#!/bin/vbash
source /opt/vyatta/etc/functions/script-template
configure
WAN_IF_GROUP=wan
show firewall group interface ${WAN_IF_GROUP} | cut -c 2- | cut -d' ' -f2 > /tmp/WANS
WANS=$(</tmp/WANS)
getip(){
        #ip -4 a show ${1} | grep -Po 'inet \K[0-9.]*'
        run show interface ${1:0:3} $1 brief  | tr -s '  ' | grep $1 | cut -d' ' -f2 | cut -d/ -f1 | grep -v ':'
        # returns like 123.234.34.34
        # grep -v : removes ipv6
}


for wan in $WANS ; do
        delete firewall group address-group ADDRv4_${wan}
        for ip in $(getip ${wan}) ; do
                set firewall group address-group ADDRv4_${wan} address $(getip ${wan})
        done
        set firewall group address-group ADDRv4_WANs include ADDRv4_${wan}
done
commit
# don't save since it's dhcp anyway?

Reading more of the fancy internal scripting going on inside VyOS, there's already both a place to put this script (that would cause it to automatically be called by dhclient upon a new address), _and_ it sets a bunch of variables for us so I don't have to hac hac hac parse output that really isn't intended to be parsed. https://github.com/vyos/vyos-1x/tree/current/src/etc/dhcp/dhclient-exit-hooks.d

Let me see if I can do something here.

okay, so with https://vyos.dev/T4997 in place (tested via my custom build https://github.com/b-/vyos-build-action/releases/tag/v1.4-rolling_bri_add-dhcp-user-hooks ) and the following file in /config/scripts/dhcp-client/post-hooks.d/set-addrgroup

#!/bin/sh
#
# /config/scripts/dhcp-client/post-hooks.d/set-addrgroup

# To enable this script set the following variable to "yes"
RUN="yes"

if [ "$RUN" = "yes" ]; then
        tmpfile=$(mktemp /tmp/dhcp-script.XXXXXXXX)
        chmod -R 755 "${tmpfile}"
        logfile=/tmp/set-addrgroup.debug
        if [[ _"$reason"_ == "_BOUND_" ]] ; then
        echo "BOUND!" >> $logfile
        echo "$tmpfile" >> $logfile
        tee  "$tmpfile" >> $logfile <<EOF
#!/usr/bin/vbash
source /opt/vyatta/etc/functions/script-template
configure
delete firewall group address-group ADDRv4_${interface}
set firewall group address-group ADDRv4_${interface} address ${new_ip_address}
commit comment "Updated address-group ADDRv4${interface} from DHCP"
save
EOF
        sg vyattacfg -c "/usr/bin/vbash -x ${tmpfile}" >> ${logfile}
        fi
fi

you can set the following in the config, and it works!

set nat destination rule 55 description 'mediabox https reflection'
set nat destination rule 55 destination group address-group 'ADDRv4_eth0'
set nat destination rule 55 destination port '443'
set nat destination rule 55 inbound-interface 'eth1'
set nat destination rule 55 log
set nat destination rule 55 protocol 'tcp'
set nat destination rule 55 translation address '172.23.217.75'
set nat destination rule 55 translation options address-mapping 'persistent'
set nat destination rule 55 translation port '443'
set nat source rule 55 destination address '172.23.217.64/26'
set nat source rule 55 outbound-interface 'eth1'
set nat source rule 55 protocol 'tcp'
set nat source rule 55 source address '172.23.217.64/26'
set nat source rule 55 translation address 'masquerade'

and it works great!

EDIT: Updated example — the auto-populated address groups are named like ADDRv4_eth0
EDIT2: updated script, added >>${logfile} to the line that actually runs the generated config script

Hello

@b- Im not sure 100% sure whats happening, as I am not a developer... I saw your code samples and PR in git...

But what I was assuming with this ticket/task/FR is a feature that allows us to assign an interface (any interface) as a destination or source in the firewall and NAT config. (backend script would translate the layer 1 address (phy or virt interface) to the assigned layer 3 address transparently)

I'm hoping its not specific to interfaces designated to "WAN" use. But rather a generic function.
With hairpin nat this would be used with the wan interfaces through context.

Regards,
Luke.

Hi @lue30499,

This isn’t specific to WANs at all, no! I am using it for a WAN, so some of my comments reflect that, but really this is just a generic hook for any DHCP interface.

I need to finish writing documentation for a user-configurable hooks directory for VyOS that will be added upstream, so the use of this function will not necessarily require a custom build.

(Ideally this feature itself would also go upstream, but I’m a new VyOS contributor so I don’t want to push for too much right away!)

Specifically, though, this is just a script that will get run every time the DHCP client is invoked, and upon receiving a DHCP lease it will set a specific firewall address-group named ADDRv4_<int> (where <int> is the name of your interface to VyOS e.g., eth0) to the IPv4 address that was received.

Thus allowing one to create a NAT rule with a destination or source of, e.g., “the IPv4 address of interface eth0,” when eth0 is set by DHCP.

Thanks,
—bri

@lue30499 T4997 was merged, so the script I put above (which adds/updates a firewall group for the DHCP IP of any DHCP-enabled interfaces) can now be installed on an official build of 1.4-rolling!

I'm going to make a Git repository for it, so that it's easier for me to improve the script and keep track of changes.

Eventually I'd like to see about it being included in to VyOS as an official feature, but I don't want to get ahead of myself. I feel like the way I'm writing and then calling the vbash configure script is a little hacky. :)

It works, though — I've been using it on my router for a few weeks now for a few DNAT rules:

vyos@vyos# for rule in 20 45 50; do echo "---nat dest rule $rule---" ; show nat dest rule $rule | commands;done
---nat dest rule 20---
set destination group address-group 'ADDRv4_WANs'
set destination port '6443'
set inbound-interface 'eth1'
set log
set protocol 'tcp'
set source group network-group 'Trusted'
set translation address '192.168.12.2'
---nat dest rule 45---
set description 'mediabox ssh'
set destination group address-group 'ADDRv4_WANs'
set destination port '42022'
set inbound-interface 'eth0'
set log
set protocol 'tcp'
set translation address '172.23.217.75'
set translation options address-mapping 'persistent'
set translation port '22'
---nat dest rule 50---
set description 'mediabox-vm dmz'
set destination group address-group 'ADDRv4_eth0'
set inbound-interface 'any'
set log
set translation address '172.23.217.75'
set translation options address-mapping 'persistent'
Viacheslav renamed this task from Dynamic ipv4 interface list to Dynamic ipv4 interface list hairpin.Jun 8 2023, 4:16 AM

Just a heads up that the above script can potentially destroy the config on boot, because if the system is fast enough, the hook will run before the boot config has finished loading. This will result either in an error when commiting because the config is locked (good outcome), or the config not being loaded at all (bad outcome). I've changed it a bit to actually wait for the boot config to load and now it runs safer, but it's still a bit janky in my opinion, I'm still hoping we can get something that works in-system for this case

#!/bin/sh
#
# /config/scripts/dhcp-client/post-hooks.d/set-addrgroup

# To enable this script set the following variable to "yes"
RUN="yes"
TIMEOUT=60
SECONDS=0
LOCKFILE=/tmp/configdone

wait_for_lock() {
  echo $(date +%s) waiting for lock >> $logfile
  until [ -f $LOCKFILE ] || (( SECONDS++ >= TIMEOUT )); do
    echo "Lock not present, waiting" >> $logfile
    sleep 1
  done
  if [ -f /tmp/configdone ]; then
    echo "Lock appeared after $SECONDS seconds" >> $logfile
  else
    echo "Timed out" >> $logfile
    exit 1
  fi
}

if [ "$RUN" = "yes" ]; then
  if [[ _"$reason"_ == "_BOUND_" ]] ; then
    logfile=/tmp/set-addrgroup-$interface.debug
    echo "BOUND on $interface" > $logfile
    tmpfile=$(mktemp /tmp/dhcp-script.XXXXXXXX)
    chmod -R 755 "${tmpfile}"
    echo "$tmpfile" >> $logfile
    tee  "$tmpfile" >> $logfile <<EOF
#!/usr/bin/vbash
source /opt/vyatta/etc/functions/script-template
configure
delete firewall group address-group ADDRv4_${interface}
set firewall group address-group ADDRv4_${interface} address ${new_ip_address}
commit comment "Updated address-group ADDRv4${interface} from DHCP"
save
EOF
  wait_for_lock
  sg vyattacfg -c "/usr/bin/vbash -x ${tmpfile}" >> ${logfile}
  rm $tmpfile
  fi
fi

You of course need something to create the lockfile, i'm using vyos-postconfig-bootup.script to make that file.

Viacheslav triaged this task as Wishlist priority.Jan 20 2024, 12:47 AM