NetCrosswalk: Cross-Vendor Network Task Translator

You know how to do it on one box. Here's how it's done on the other — GUI or CLI, plus the gotchas.

30 tasks
Firewall & NAT

Port forward to an internal server (destination NAT)

Make an internal service reachable from outside by translating an external port to an internal host.

Why it differs: Every vendor splits this into 'translate' and 'permit', but they disagree about whether the permit rule matches the pre-NAT or post-NAT address, and whether the two halves are created together or separately. That one detail decides whether it works.

Router / switch / AP

MikroTik RouterOS

GUI or CLI
Written against RouterOS 7.x

Steps

  1. NAT: IP → Firewall → NAT → + → *Chain* dstnat, *Protocol* tcp, *Dst. Port* 8080, *In. Interface* ether1Action tab → dst-nat, *To Addresses* 192.168.88.10, *To Ports* 80.
  2. CLI: /ip firewall nat add chain=dstnat action=dst-nat protocol=tcp dst-port=8080 in-interface=ether1 to-addresses=192.168.88.10 to-ports=80 comment="web fwd"
  3. Then permit it in the filter (if you have a drop rule): /ip firewall filter add chain=forward action=accept protocol=tcp dst-address=192.168.88.10 dst-port=80 place-before=0
  4. Test from outside, not from the LAN.

Gotchas & notes

  • The filter rule must match the internal address and port (192.168.88.10:80), because dstnat has already rewritten the packet by the time the forward chain sees it. Matching the public IP:8080 there silently never fires.
  • Use in-interface=ether1 (or better, in-interface-list=WAN) rather than dst-address=<public ip> if your WAN IP is dynamic.
  • Hairpin NAT is not automatic. LAN clients using the public hostname will fail until you add a matching srcnat rule: /ip firewall nat add chain=srcnat action=masquerade src-address=192.168.88.0/24 dst-address=192.168.88.10 dst-port=80 protocol=tcp.
  • Testing from inside the LAN and concluding the forward is broken is extremely common. Use a phone on mobile data.
  • Make sure your masquerade srcnat rule is not also catching this traffic in a way that hides the real source IP from the server.
Vendor documentation ↗
NGFW / router

Fortinet FortiGate (FortiOS)

GUI or CLI
Written against FortiOS 7.2 – 7.6

Steps

  1. Create the VIP: Policy & Objects → Virtual IPs → Create New → Virtual IP. Set *Interface* (WAN), *External IP Address/Range*, *Map to IPv4 Address/Range* = internal IP. Tick Port Forwarding, set *External Service Port* 8080 and *Map to Port* 80.
  2. Create a policy that uses it: Policy & Objects → Firewall Policy → Create New with *Incoming Interface* WAN, *Outgoing Interface* LAN, *Source* all, Destination = the VIP object, *Service* as needed, *Action* ACCEPT.
  3. Leave the policy's NAT toggle off (that toggle is source NAT, which you do not want here).
  4. CLI: config firewall vipedit "web-vip"set extip 203.0.113.10set mappedip 192.168.1.10set extintf "port1"set portforward enableset extport 8080set mappedport 80nextend

Gotchas & notes

  • A VIP on its own does nothing. It is only a translation object; without a firewall policy referencing it as the destination, no traffic passes. Half of all 'my FortiGate port forward does not work' cases are a missing policy.
  • In the policy, the destination is the VIP object, and FortiOS matches on the pre-NAT external address automatically. Do not create a separate address object for the internal IP and use that instead — it will not match.
  • The NAT checkbox in the policy is source NAT. Enabling it makes traffic appear to the internal server as coming from the FortiGate, hiding real client IPs and breaking server-side logging.
  • A VIP with no portforward maps *all* ports — a static 1:1 NAT. Tick Port Forwarding unless you really mean to expose the whole host.
  • VIPs appear in the address-object picker elsewhere in the GUI, which lets you accidentally use one as a source. Name them with a vip- prefix.
  • For LAN clients reaching the service by public name, either enable *NAT46*-style hairpin via a second policy or, better, use split-DNS on your internal resolver.
Vendor documentation ↗
Controller-managed gateway / switch / AP

Ubiquiti UniFi Network

GUI
Written against UniFi Network 8.x – 9.x (UDM / UDM-Pro / UXG)

Steps

  1. Settings → Routing & Firewall → Port Forwarding (older) or Settings → Security → Port Forwarding (newer) → Create New Forwarding Rule.
  2. Set *Name*, *From* (Any or a limited source), *Port* (external), *Forward IP* (internal host), *Forward Port*, *Protocol*.
  3. Save — UniFi creates the matching firewall permit automatically.
  4. Verify from an external network.

Changed across versions

  • 8.x and earlierSettings → Security → Port Forwarding (older builds: *Settings → Routing & Firewall → Port Forwarding*).
  • 9.3Folded into the zone-based firewall UI: Settings → Policy Table → Create New Policy → Port Forwarding.
  • 9.4+Renamed again to Settings → Policy Engine → Port Forwarding. This menu has moved on almost every 9.x point release — if it's not where a guide says, search Settings for "forward".

Gotchas & notes

  • This is the simplest port forward of the six: UniFi creates the NAT and the firewall rule together, so there is no second step to forget.
  • The auto-created firewall rule is placed above your manual rules with a reserved index. If you have a manual block rule you expect to take precedence, it will not.
  • Set *From* to specific addresses rather than Any when you can — the field is easy to skip and defaults to fully open.
  • Give the internal host a static DHCP reservation first (Client → Fixed IP), or the forward will point at an address that eventually moves.
  • Hairpin/NAT-reflection support has varied by firmware. If internal clients cannot reach the public name, use internal DNS rather than fighting the gateway.
Vendor documentation ↗
Router / switch

Cisco IOS / IOS-XE

CLI
Written against IOS-XE 17.x (Catalyst 9000, ISR 1000/4000)

Steps

  1. Mark the interfaces: interface Gi0/1ip nat inside, and interface Gi0/0ip nat outside.
  2. Static PAT entry: ip nat inside source static tcp 192.168.1.10 80 interface GigabitEthernet0/0 8080
  3. Or with an explicit public IP: ip nat inside source static tcp 192.168.1.10 80 203.0.113.10 8080 extendable
  4. Permit it in the inbound ACL, matching the post-NAT inside address: permit tcp any host 192.168.1.10 eq 80
  5. Verify: show ip nat translations and show ip nat statistics

Gotchas & notes

  • Order of operations on IOS: for traffic arriving on an outside interface, NAT is translated before the inbound ACL is evaluated, so your ACL must permit the inside (private) address. Writing the ACL against the public IP is the classic failure.
  • Forgetting ip nat inside / ip nat outside on the interfaces means the translation entry exists in the config and is never applied. show ip nat statistics showing zero hits is the tell.
  • The extendable keyword is needed when several static entries share one public address; without it IOS may reject the second entry.
  • interface GigabitEthernet0/0 in the static command tracks a dynamic DHCP/PPPoE WAN address automatically — use that form on a dynamic WAN instead of hardcoding an IP.
  • Clear stale translations after a change with clear ip nat translation *, otherwise the old mapping persists for the timeout.
Vendor documentation ↗
NGFW

Palo Alto Networks PAN-OS

GUI or CLI
Written against PAN-OS 10.2 / 11.x

Steps

  1. Policies → NAT → Add. *Original Packet*: source zone untrust, destination zone untrust, destination interface, destination address = the public IP. *Translated Packet* → Destination Address Translation: translated address = internal IP, translated port = 80.
  2. Policies → Security → Add: source zone untrust, destination zone dmz/trust (the post-NAT zone), destination address = the public IP (pre-NAT), application/service as needed, Allow.
  3. Commit.
  4. Verify with test nat-policy-match and test security-policy-match, then check Monitor → Traffic.

Gotchas & notes

  • The rule that catches everyone: in the NAT policy the destination zone is the *pre-NAT* zone (untrust), but in the Security policy the destination zone is the *post-NAT* zone (where the server lives) while the destination address is still the *pre-NAT* public IP. Get either half wrong and the session is denied by interzone-deny.
  • PAN-OS evaluates NAT before security policy but performs the translation after — hence the split above. It is documented, non-obvious, and the reason to always verify with test security-policy-match.
  • Two separate policies plus a commit means three chances to be half-finished. Use the Device → Troubleshooting testers before generating traffic.
  • For internal clients reaching the public IP you need a U-Turn NAT policy (source and destination both internal, with source translation) — PAN-OS does not do hairpin implicitly.
  • Do not tick *Bi-directional* on a destination-NAT rule; that option belongs to static source NAT and creates a translation you did not intend.
Vendor documentation ↗
Router / firewall (FreeBSD)

Netgate pfSense CE

GUI
Written against pfSense CE 2.7 / Plus 24.x

Steps

  1. Firewall → NAT → Port Forward → Add.
  2. Set *Interface* WAN, *Protocol* TCP, *Destination* WAN address, *Destination port range* 8080, *Redirect target IP* 192.168.1.10, *Redirect target port* 80.
  3. Leave *Filter rule association* on Add associated filter rule — this creates and links the WAN pass rule for you.
  4. SaveApply Changes. Confirm the linked rule appears on Firewall → Rules → WAN.

Gotchas & notes

  • *Filter rule association* is the field that matters. Add associated filter rule keeps the pass rule in sync with the forward forever; *Pass* creates no rule but bypasses filtering entirely (avoid); *None* leaves you to write the rule yourself.
  • The associated filter rule is auto-written against the internal IP, since pf translates before filtering. If you write your own, match the internal address.
  • Use WAN address rather than a literal IP for the destination so the forward survives a WAN IP change.
  • NAT Reflection for hairpin access is off by default: enable it per-rule (*NAT reflection* → NAT + Proxy or Pure NAT) or globally in System → Advanced → Firewall & NAT. Pure NAT plus 'Enable automatic outbound NAT for Reflection' is the usual combination.
  • 1:1 NAT is a separate tab and forwards every port for a host — do not reach for it when you only need one port.
  • Give the target host a static DHCP mapping first.
Vendor documentation ↗
NGFW

SonicWall (SonicOS)

GUI
Written against SonicOS 7.3.x (Gen7 TZ/NSa/NSsp; current General Release line is 7.3.2/7.3.3 — note Gen8 TZ80/TZ280+/NSa 2800+ hardware instead runs the separate SonicOS 8.x line)

Steps

  1. Create an Address Object for the internal server: Object → Address Objects → Add (Host, zone = LAN/DMZ).
  2. Easiest path: launch Wizards → Public Server Wizard (wizard icon in the top toolbar). It creates the inbound NAT Policy, the matching Access Rule, and an internal loopback NAT policy together in one pass.
  3. Manual path: Policy → Rules and Policies → NAT Policies → Add. Set *Original Source* = Any, *Original Destination* = the WAN address object (e.g. WAN Primary IP), *Original Service* = the external service, *Translated Source* = Original, *Translated Destination* = the internal server object, *Translated Service* = Original (or the internal port, if port-translating).
  4. Add the matching rule: Policy → Rules and Policies → Access Rules, WAN→LAN (or WAN→DMZ) tab → Add. Set *Destination* to the original WAN address object, not the internal server, *Service* to match, *Action* = Allow.
  5. If internal clients also need to reach the service via the public IP/FQDN, add a separate Loopback NAT policy (the Public Server Wizard does this automatically; doing it manually means adding a LAN→LAN or WAN→LAN reflexive NAT entry yourself).

Changed across versions

  • SonicOS 6.5 (Gen6)NAT Policies live at Network → NAT Policies and Access Rules at Firewall → Access Rules, rather than under the Gen7 'Rules and Policies' grouping. The Public Server Wizard exists on both generations.

Gotchas & notes

  • The single biggest gotcha on this platform: the Access Rule's Destination must match the original (pre-translation) address — typically the WAN interface IP — not the internal server's address object. Someone coming from pfSense (where the port-forward wizard's auto-generated rule matches the internal host) or a newer Cisco ASA (which matches real/post-NAT addresses) will naturally point the rule at the internal server, and it will never match.
  • NAT translation and permission are two entirely separate objects here — a NAT Policy with no matching Access Rule silently drops the traffic after translating it; an Access Rule with no NAT Policy just forwards untranslated packets to a host that never receives them. Both symptoms look identical from the outside ('nothing connects').
  • Loopback/reflexive NAT for internal clients hitting the external name/IP is not automatic unless you use the Public Server Wizard — a common support call is 'it works from outside but not from inside.'
  • NAT Policy configuration has no CLI equivalent whatsoever; this entire task is GUI-only.
Vendor documentation ↗
Cloud-managed business routers, switches & Orbi Pro/WiFi APs

NETGEAR Insight (Cloud Management Platform)

GUI
Written against Insight Cloud Portal/App 10.0.x (cloud mgmt); switch-side CLI varies by line — Smart Switch "Lite CLI" firmware 6.0.10.5+/7.0.9.5+, fully-managed M4200/M4300 CLI 12.0.11.x

Steps

  1. Insight app or portal → Locations → select the router (BR-series) or Orbi Pro (router mode) → Security → Port Forwarding → Add Custom Service.
  2. Enter a service name, protocol (TCP/UDP/Both), the external (WAN) port or range, the internal device's LAN IP, and the internal port or range. There are also a few pre-defined common services (e.g. common game/app ports) you can pick instead of building a custom one.
  3. Save/Apply — Insight pushes the change to the cloud-adopted device, which typically takes effect within seconds to a couple of minutes depending on device check-in interval.
  4. There is no separate step to write a matching 'allow' rule — the single Port Forwarding entry is both the DNAT and the permit, evaluated against the pre-translation (public) address/port as you enter it.
  5. No CLI path exists for this on routers/Orbi Pro — it is Insight-only, full stop.

Gotchas & notes

  • Because the translate and permit halves are fused into one object, you can't accidentally create a NAT with no matching firewall hole (a classic mistake on split-model platforms) — but you also can't reuse a NAT mapping under a different security policy, since there is no policy layer to separate.
  • The internal target IP must be a static LAN address (or a DHCP reservation) — if the internal host's IP changes, the forward silently keeps pointing at the old address and traffic just stops arriving, with nothing in the GUI flagging it.
  • Insight enforces changes through the cloud — if the router/AP loses its connection to the Insight backend, you cannot push or edit port forwards until connectivity is restored (there's no local fallback GUI on the router itself the way there is on a Smart Switch).
  • Smart Switches and M4200/M4300 have no port-forwarding concept at all — they're not routers, so this whole task is out of scope for that side of the NETGEAR Insight lineup.
Vendor documentation ↗