By Manny Fernandez

July 16, 2026

The Cisco Engineer’s Guide to FortiGate: A Side-by-Side CLI Rosetta Stone for IOS Veterans Landing on FortiOS

You have twenty years of muscle memory. You type conf t before your coffee finishes brewing. Then someone hands you a FortiGate and the first thing you notice is that enable does nothing, write memory does nothing, and show run returns a config tree that looks like somebody exported a JSON blob into a terminal.

The good news is that nothing you know is wasted. A firewall policy is an ACL with better manners. A phase1-interface is a VTI. An accprofile is a privilege level. VDOMs are contexts. The concepts map almost one to one. What changes is the grammar, and the grammar is learnable in an afternoon.

This guide is that afternoon. Every section below puts the Cisco command you already know on the left and the FortiOS equivalent on the right. No theory, no marketing, no “paradigm shifts.” Just the config that works, in the order you are likely to need it: get in the box, get an interface up, get traffic through, get it routed, get it encrypted, then get it troubleshot at 2 a.m.

One note before you start. Everything here is FortiOS 7.x CLI. Where a command changed between versions I say so. Where FortiOS genuinely has no equivalent to a Cisco feature, I say that too rather than inventing one.

1. The Mental Model: What Actually Changes

Before a single command, understand the four things that will trip you up. Everything else is syntax.

1. FortiOS splits the CLI into four verbs, not two modes. Cisco has EXEC and config. FortiOS has config (change the config), get (read runtime state), show (read the config), and execute (do a thing right now, like ping or reboot). There is no enable. You log in already privileged, scoped by your access profile.

2. The firewall policy is the ACL, and it is global and ordered. You do not apply anything to an interface. You write one ordered list of policies and each one names a source interface and a destination interface. First match wins, and there is an implicit deny at the bottom, exactly like an ACL.

3. A FortiGate is a firewall, not a router that happens to filter. Two interfaces in the same VDOM do not pass traffic to each other just because a route exists. If there is no policy, there is no traffic. This is the single biggest cause of “but the route is right there” tickets from Cisco engineers.

4. You cannot type a raw IP address into a policy. FortiOS is object oriented. Addresses, services, and schedules are objects you create first and reference second. It feels like extra work on day one and saves you on day one hundred.

1.1 The command taxonomy
What you are trying to do FortiOS verb
Change the configuration (“conf t”) config <tree>
Read the configuration (“show run”) show
Read runtime state (“show ip route”, “show version”) get
Do something now (“ping”, “reload”, “copy”) execute
Deep debug (“debug ip packet”) diagnose

Field note: Internalize this table and 80 percent of your “what is the command for X” questions answer themselves. If it is state, it is get. If it is an action, it is execute. If it hurts performance, it is diagnose.

1.2 The concept map
Cisco concept FortiGate equivalent
Extended ACL applied to an interface Firewall policy (config firewall policy)
object-group network / ASA object network config firewall address, config firewall addrgrp
object-group service config firewall service custom, config firewall service group
Zone-based firewall zone config system zone
VRF VDOM (full separation) or VRF (routing only, set vrf on the interface)
Security context (ASA) VDOM
SVI (interface Vlan10) VLAN interface (set type vlan, set vlanid 10)
Port-channel / EtherChannel Aggregate interface (set type aggregate)
Tunnel interface / VTI phase1-interface (route-based IPsec)
crypto map Policy-based IPsec (legacy, avoid on new builds)
HSRP / VRRP VRRP (under config system interface)
StackWise / VSS / ASA failover FGCP HA cluster (config system ha)
ip helper-address set dhcp-relay-service enable
ip nat inside source … overload set nat enable inside the firewall policy
ip nat inside source static (inbound) VIP (config firewall vip)
ip nat pool IP pool (config firewall ippool)
Null0 route set blackhole enable on a static route
Loopback0 Interface with set type loopback
privilege level 15 accprofile super_admin
access-class on line vty set trusthost1..10 per admin
show tech-support execute tac report
NetFlow config system netflow (NetFlow v9 / IPFIX), config system sflow
2. CLI Survival: Getting Around the Box

Start here. If you can navigate the tree and read the config, everything else is lookup.

2.1 Entering configuration
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
enable
configure terminal
!
! You are now in global config.
! Everything hangs off this one mode.
# No enable. No configure terminal.
# You log in already at the root prompt,
# scoped by your access profile.

config system global
    set hostname "FGT-EDGE-01"
end

Field note: FortiOS has no global config mode. You descend into a specific tree, make changes, and come back out. The tree IS the mode.

2.2 The edit / next / end pattern
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! Cisco: sub-config is implicit
interface GigabitEthernet0/1
 description WAN
exit
interface GigabitEthernet0/2
 description LAN
exit
!
config system interface
    edit "port1"
        set alias "WAN"
    next            <-- save this object,
                        stay in the tree
    edit "port2"
        set alias "LAN"
    next
end                 <-- commit and leave

Field note: This is the single most important pattern in FortiOS. “next” closes the object you are editing and keeps you in the tree. “end” closes the tree and commits. Forgetting “next” before “end” still works in most trees, but forgetting it in the middle of a multi-object edit will bite you.

2.3 Backing out without saving
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! Ctrl+Z or "end" jumps to EXEC.
! Changes are already live in
! running-config. Your undo is
! "no <command>" or reload without
! having written memory.

end
config system interface
    edit "port1"
        set ip 10.99.99.99 255.255.255.0
    abort   <-- discards, exits the tree

# abort is your friend. Learn it before
# you learn anything else.

Field note: There is no “reload in 5” safety net habit on FortiOS the way there is on IOS. “abort” is the closest thing to Ctrl+C with intent. If you have already typed “end”, it is live and saved.

2.4 Viewing the configuration
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show running-config
show running-config interface Gi0/1
show startup-config
show running-config | include ospf
show running-config | section router bgp
show
show system interface port1
# There is no startup-config.
# The running config IS the saved config.

show | grep ospf
show router bgp

# See defaults too:
show full-configuration
show full-configuration system global

Field note: “show” only prints values that differ from default. This is a feature, not a bug, but it means a setting you swear you configured may be invisible because it matches the default. “show full-configuration” prints everything, and it is enormous. Filter it.

 2.5 Saving the configuration
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
copy running-config startup-config
! or
write memory
! or
wr
# There is no save command.
# "end" writes to flash immediately.

# Back up off-box instead:
execute backup config tftp fgt.conf 10.1.1.9
execute backup config ftp fgt.conf 10.1.1.9

# Config revisions on flash:
execute revision list config
execute restore config flash 12

Field note: This is the change that gets people hurt. On IOS you can experiment freely and reload out of trouble. On FortiOS, “end” is “wr mem”. There is no unsaved state to abandon. Back up before you touch anything, and use config revisions on models with the flash space for them.

2.6 Filtering output
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show running-config | include vlan
show running-config | exclude !
show running-config | begin router bgp
terminal length 0
show | grep vlan
show | grep -i VLAN

# Grab context around the hit:
show | grep -f "port1"        # full object
show | grep -A 5 "edit \"port1\""
show | grep -B 2 "set ip"

# Kill the pager:
config system console
    set output standard
end

Field note: “grep -f” is the one you will use constantly. It prints the entire configuration object containing the match, not just the matching line. That is the FortiOS answer to “show run | section”.

2.7 Version, uptime, and health
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show version
show inventory
show processes cpu sorted
show processes memory
show environment
get system status
get hardware status
get system performance status
diagnose sys top 5 20
diagnose hardware sysinfo memory
execute sensor list

Field note: “get system status” is your “show version”: serial number, firmware, build, HA status, VDOM mode, and license state in one screen. “diagnose sys top 5 20” refreshes every 5 seconds and shows the top 20 processes. Press “q” to quit, not Ctrl+C.

2.8 Ping and traceroute
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ping 8.8.8.8
ping 8.8.8.8 source Loopback0 repeat 100
ping 8.8.8.8 size 1472 df-bit
traceroute 8.8.8.8
execute ping 8.8.8.8

# Options are sticky and set separately:
execute ping-options source 10.1.1.1
execute ping-options repeat-count 100
execute ping-options data-size 1472
execute ping-options df-bit yes
execute ping 8.8.8.8

execute ping-options reset   # undo them
execute traceroute 8.8.8.8

Field note: FortiOS ping options are sticky. Set a source address for one test and it stays set for every ping in that session until you reset it. Engineers lose hours to this. Get in the habit of “execute ping-options reset” when you are done.

2.9 Reboot and shutdown
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
reload
reload in 5
reload cancel
execute reboot
execute shutdown

# No "reload in 5" equivalent.
# No confirmation rollback.
# Have out-of-band access.

Field note: There is no scheduled-reload safety net. If you are changing management access remotely, have console or a second path first.

3. Admin Accounts and Management Access

Privilege levels become access profiles. VTY ACLs become trusted hosts. Everything else is close enough to feel familiar.

3.1 Creating a local admin
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
username mfernandez privilege 15 secret S3cretPass
!
aaa new-model
aaa authentication login default local
aaa authorization exec default local
config system admin
    edit "mfernandez"
        set accprofile "super_admin"
        set vdom "root"
        set password S3cretPass
    next
end

Field note: super_admin is the built-in “privilege 15”. prof_admin is read-write within a single VDOM. Build your own for anything narrower.

3.2 Privilege levels vs access profiles
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
privilege exec level 5 show running-config
privilege exec level 5 show interfaces
username noc privilege 5 secret Watch3r
config system accprofile
    edit "NOC-READONLY"
        set secfabgrp read
        set ftviewgrp read
        set fwgrp read
        set loggrp read
        set netgrp read
        set sysgrp read
        set vpngrp read
        set utmgrp read
        set authgrp read
        set wifi read
    next
end

config system admin
    edit "noc"
        set accprofile "NOC-READONLY"
        set password Watch3r
    next
end

Field note: Access profiles are grouped by function, not by numbered level. Each group can be none, read, or read-write. This is far more granular than privilege levels and far less painful than IOS privilege command mapping.

3.3 Restricting management source addresses
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
access-list 10 permit host 10.1.1.5
access-list 10 permit 10.1.1.0 0.0.0.255
access-list 10 deny any log
!
line vty 0 4
 access-class 10 in
 transport input ssh
config system admin
    edit "mfernandez"
        set trusthost1 10.1.1.5 255.255.255.255
        set trusthost2 10.1.1.0 255.255.255.0
    next
end

Field note: Trusted hosts are per admin account, not per line. If you leave trusthost1 at 0.0.0.0 0.0.0.0, that account is reachable from anywhere the interface allows. Set them on every account or none of them do any good, because an admin with no trusthost is a hole around the ones that have them.

3.4 Enabling management protocols
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip ssh version 2
ip http secure-server
no ip http server
!
line vty 0 4
 transport input ssh
 exec-timeout 10 0
# Management access is a property of the
# INTERFACE, not a global service.

config system interface
    edit "port2"
        set allowaccess ping https ssh snmp
    next
end

config system global
    set admin-sport 8443
    set admin-ssh-port 2222
    set admintimeout 10
    set admin-scp enable
end

Field note: This catches everyone. You do not turn on SSH globally, you allow it per interface with set allowaccess. And “set allowaccess” is a replace, not an append. Typing “set allowaccess ssh” on an interface that had “ping https ssh” leaves you with ssh only. Always type the full list.

3.5 TACACS+ and RADIUS admin authentication
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
tacacs server TAC1
 address ipv4 10.1.1.20
 key Sh4redK3y
!
aaa group server tacacs+ TACGRP
 server name TAC1
!
aaa authentication login default group TACGRP local
aaa authorization exec default group TACGRP local
config user tacacs+
    edit "TAC1"
        set server "10.1.1.20"
        set key Sh4redK3y
        set authorization enable
    next
end

config user group
    edit "TACACS-ADMINS"
        set member "TAC1"
    next
end

# Wildcard admin: any user the server
# accepts gets this profile.
config system admin
    edit "tacacs-admins"
        set remote-auth enable
        set accprofile "super_admin"
        set wildcard enable
        set remote-group "TACACS-ADMINS"
    next
end

Field note: The wildcard admin object is the pattern. You are not creating a user, you are creating a rule that says “anyone this server authenticates gets this profile in this VDOM.” Local accounts still work as fallback if the server is unreachable.

3.6 Login banner
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
banner motd ^
AUTHORIZED USE ONLY
^
!
banner login ^
Access is monitored.
^
config system global
    set pre-login-banner enable
    set post-login-banner enable
end

# The text itself lives in replacemsg:
config system replacemsg admin \
  pre_admin-disclaimer-text
    set buffer "AUTHORIZED USE ONLY"
end

Field note: The switch and the text are in two different places. Enabling the banner without setting the buffer gives you the Fortinet default text, which your auditor will not accept.

4. Interfaces

Physical interfaces are called port1, port2, and so on, or by their FortiOS names on larger boxes (x1, ha1, mgmt). They are up by default. There is no no shutdown.

4.1 Assigning an IP address
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface GigabitEthernet0/1
 description WAN-to-ISP-A
 ip address 203.0.113.2 255.255.255.0
 no shutdown
!
config system interface
    edit "port1"
        set alias "WAN-to-ISP-A"
        set mode static
        set ip 203.0.113.2 255.255.255.0
        set allowaccess ping
        set role wan
        set status up
    next
end

Field note: Interfaces come up enabled. “set status up” is the default, so you rarely type it. “set alias” is your description. “set role” (wan, lan, dmz, undefined) only changes which fields the GUI shows you, but the GUI is unusable without it, so set it.

4.2 DHCP and PPPoE client
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface GigabitEthernet0/0
 ip address dhcp
!
interface Dialer1
 encapsulation ppp
 ip address negotiated
 ppp chap hostname user@isp
 ppp chap password S3cret
config system interface
    edit "port1"
        set mode dhcp
        set defaultgw enable
        set distance 5
    next
end

config system interface
    edit "port1"
        set mode pppoe
        set username "user@isp"
        set password S3cret
        set defaultgw enable
    next
end

Field note: “set defaultgw enable” is what installs the learned default route. “set distance 5” sets the AD of that learned route so you can prefer or deprefer it against a static.

4.3 Speed, duplex, and MTU
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface GigabitEthernet0/1
 speed 1000
 duplex full
 mtu 9216
 ip mtu 1400
!
config system interface
    edit "port1"
        set speed 1000full
        set mtu-override enable
        set mtu 1400
    next
end

# Valid values vary by NIC:
config system interface
    edit "port1"
        set speed ?
    next
end

Field note: Speed and duplex are one setting: 1000full, 100full, 10half, auto. And MTU needs mtu-override enable first, or “set mtu” is silently ignored. That silent ignore has cost me an hour more than once.

4.4 Loopbacks
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface Loopback0
 ip address 10.255.255.1 255.255.255.255
!
config system interface
    edit "lb0"
        set vdom "root"
        set ip 10.255.255.1 255.255.255.255
        set allowaccess ping
        set type loopback
    next
end

Field note: A loopback on a FortiGate is a real interface for policy purposes. If you want to manage the box on it, you still need a policy allowing traffic to it, plus allowaccess. This is a common gotcha when moving BGP peering to loopbacks.

4.5 Link aggregation (LACP)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface Port-channel1
 description To-Core
 ip address 10.1.1.1 255.255.255.252
!
interface range Gi0/3 - 4
 channel-group 1 mode active
 channel-protocol lacp
!
config system interface
    edit "ae1"
        set vdom "root"
        set type aggregate
        set member "port3" "port4"
        set lacp-mode active
        set lacp-speed slow
        set algorithm L4
        set ip 10.1.1.1 255.255.255.252
        set alias "To-Core"
    next
end

Field note: Member ports must have no IP, no VDOM assignment conflicts, and no references anywhere else before you can add them. FortiOS will refuse with an unhelpful error if port3 is referenced by so much as a stale policy. “diagnose sys checkused system.interface.name port3” tells you who is holding it.

4.6 Secondary addresses
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface GigabitEthernet0/2
 ip address 10.1.1.1 255.255.255.0
 ip address 10.1.2.1 255.255.255.0 secondary
!
config system interface
    edit "port2"
        set ip 10.1.1.1 255.255.255.0
        set secondary-IP enable
        config secondaryip
            edit 1
                set ip 10.1.2.1 255.255.255.0
                set allowaccess ping
            next
        end
    next
end

Field note: Note the capital IP in “secondary-IP”. FortiOS is case sensitive here and nowhere else that matters. Yes, it is annoying.

4.7 Zones (grouping interfaces for policy)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! Zone-based firewall (ZBF)
zone security INSIDE
zone security OUTSIDE
!
interface GigabitEthernet0/2
 zone-member security INSIDE
!
interface GigabitEthernet0/3
 zone-member security INSIDE
!
config system zone
    edit "INSIDE"
        set intrazone allow
        set interface "port2" "port3"
    next
end

# Now policies use "INSIDE" as srcintf
# instead of listing both ports.

Field note: “set intrazone allow” means traffic between port2 and port3 needs no policy. The default is deny, which matches Cisco ZBF behavior. Zones are optional on FortiGate but they keep your policy table from exploding on a box with 20 VLANs.

4.8 Checking interface state
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show ip interface brief
show interfaces GigabitEthernet0/1
show interfaces status
show interfaces counters errors
get system interface physical
get system interface
diagnose hardware deviceinfo nic port1

# Live throughput per interface:
diagnose netlink interface list port1
get system arp

Field note: “diagnose hardware deviceinfo nic port1” is your “show interfaces”: link state, speed, duplex, and every error counter the NIC driver exposes. This is where you find the CRC errors that explain why the customer thinks the firewall is slow.

5. VLANs and Switching

Here is the hard truth: a FortiGate is not a switch. It has no MAC address table in the normal sense, no VTP, no STP participation by default, and no “switchport” command. VLAN interfaces are router subinterfaces. If you need real switching, that is what FortiSwitch and FortiLink are for.

5.1 Creating a VLAN interface (the router-on-a-stick model)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
vlan 10
 name USERS
!
interface Vlan10
 ip address 10.10.10.1 255.255.255.0
 no shutdown
!
interface GigabitEthernet0/2
 switchport mode trunk
 switchport trunk allowed vlan 10,20
!
config system interface
    edit "VLAN10-USERS"
        set vdom "root"
        set ip 10.10.10.1 255.255.255.0
        set allowaccess ping
        set role lan
        set interface "port2"
        set vlanid 10
    next
end

# port2 is now implicitly a trunk.
# No "switchport mode trunk" needed.
# No "allowed vlan" list. The VLANs you
# create ON port2 are the allowed list.

Field note: This is the closest thing to a paradigm shift in this document. There is no trunk configuration. A physical port becomes a trunk the moment you create a tagged VLAN interface on it. The set of VLAN interfaces parented to that port IS the allowed VLAN list. Untagged traffic on that port hits the parent interface (port2) directly, which is your native VLAN.

5.2 Software switch (bridging ports together)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! On an ISR/switch this is just
! access ports in the same VLAN.
interface range Gi0/3 - 6
 switchport mode access
 switchport access vlan 1
!
interface Vlan1
 ip address 192.168.1.1 255.255.255.0
!
config system switch-interface
    edit "lan-sw"
        set vdom "root"
        set member "port3" "port4" "port5"
        set intra-switch-policy implicit
    next
end

config system interface
    edit "lan-sw"
        set ip 192.168.1.1 255.255.255.0
        set allowaccess ping https ssh
    next
end

Field note: “set intra-switch-policy implicit” means ports in the switch talk to each other without a policy, which is what you expect from a switch. Set it to “explicit” and you need policies between members, which is occasionally useful and usually a support ticket. Software switches run in the CPU on most models. Hardware switches (config system virtual-switch on models that have them) do it in ASIC.

5.3 Managed FortiSwitch ports (the real answer)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface GigabitEthernet1/0/10
 description Accounting-PC
 switchport mode access
 switchport access vlan 10
 switchport voice vlan 20
 spanning-tree portfast
!
config switch-controller managed-switch
    edit "S248EF0000000000"
        config ports
            edit "port10"
                set description "Accounting-PC"
                set vlan "VLAN10-USERS"
                set allowed-vlans "VLAN20-VOICE"
                set untagged-vlans "VLAN10-USERS"
                set stp-state enabled
                set edge-port enabled
            next
        end
    next
end

Field note: When a FortiSwitch is managed by the FortiGate over FortiLink, you configure switch ports from the FortiGate CLI. “set vlan” is the native/untagged VLAN (your access VLAN), “set allowed-vlans” is the tagged list (your trunk allowed list), and “set edge-port enabled” is portfast. Note it references the VLAN interface NAME, not the VLAN ID. That is the part that trips people up.

5.4 Verifying VLANs and MAC learning
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show vlan brief
show interfaces trunk
show mac address-table
show mac address-table interface Gi0/2
get system interface | grep -A 2 vlanid
show system interface | grep -f vlanid

# MAC table of a software switch:
diagnose netlink brctl name host lan-sw

# FortiSwitch MAC table via the controller:
diagnose switch-controller switch-info mac-table
get switch-controller managed-switch status

Field note: If you are looking for “show mac address-table” on a bare FortiGate with no switch interfaces and no FortiSwitch, you will not find it, because there is no bridging happening. That is not a missing feature. That is the box telling you it is a router.

6. Firewall Policy (Your ACLs, Rebuilt)

This is where the two platforms diverge most and where the FortiGate is honestly better. Instead of an ACL per interface per direction, you get one ordered, stateful, named, logged, object-based list. Same first-match-wins logic, same implicit deny, far better visibility.

6.1 Address objects (do this first)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! IOS uses inline ACL entries.
ip access-list extended LAN_OUT
 permit tcp 10.1.1.0 0.0.0.255 any eq 443
!
! ASA uses objects:
object network LAN_NET
 subnet 10.1.1.0 255.255.255.0
!
object-group network SERVERS
 network-object host 10.1.1.10
 network-object host 10.1.1.11
config firewall address
    edit "LAN_NET"
        set subnet 10.1.1.0 255.255.255.0
    next
    edit "SRV-WEB-01"
        set subnet 10.1.1.10 255.255.255.255
    next
    edit "SRV-WEB-02"
        set subnet 10.1.1.11 255.255.255.255
    next
end

config firewall addrgrp
    edit "SERVERS"
        set member "SRV-WEB-01" "SRV-WEB-02"
    next
end

Field note: Subnet masks, not wildcard masks. FortiOS accepts “10.1.1.0 255.255.255.0” or “10.1.1.0/24” and normalizes to the former. If you type a wildcard mask out of habit you will build an object for a subnet nobody has ever heard of, and it will not error.

6.2 Service objects
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
object-group service WEB tcp
 port-object eq 80
 port-object eq 443
!
object-group service MGMT
 service-object tcp destination eq 22
 service-object udp destination eq 161
config firewall service custom
    edit "TCP-8443"
        set tcp-portrange 8443
    next
    edit "APP-CUSTOM"
        set tcp-portrange 9000-9010
        set udp-portrange 9100
    next
end

config firewall service group
    edit "WEB"
        set member "HTTP" "HTTPS"
    next
end

Field note: FortiOS ships with a large library of predefined services (HTTP, HTTPS, DNS, SSH, ALL_TCP, and so on). Check “show firewall service predefined” before building your own. Note the source port syntax: “set tcp-portrange 8443:1024-65535” is dest:source, which is backwards from what most people guess.

6.3 The policy itself
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip access-list extended LAN_OUT
 permit tcp 10.1.1.0 0.0.0.255 any eq 443 log
 permit tcp 10.1.1.0 0.0.0.255 any eq 80
 deny   ip any any log
!
interface GigabitEthernet0/2
 ip access-group LAN_OUT in
!
! Stateful return traffic needs
! reflexive ACLs or CBAC/ZBF.
config firewall policy
    edit 0
        set name "LAN-to-Internet"
        set srcintf "port2"
        set dstintf "port1"
        set action accept
        set srcaddr "LAN_NET"
        set dstaddr "all"
        set schedule "always"
        set service "HTTP" "HTTPS"
        set nat enable
        set logtraffic all
        set utm-status enable
        set ssl-ssh-profile "certificate-inspection"
        set av-profile "default"
        set ips-sensor "default"
        set application-list "default"
    next
end

Field note: Note what you get for free: it is stateful (no reflexive ACL), NAT is a checkbox, and inspection profiles bolt straight on. “edit 0” tells FortiOS to auto-assign the next policy ID. Also note there is no “in” or “out”. Direction is implied by srcintf and dstintf, and the policy only matches the first packet of a session.

6.4 Policy order and moving rules
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! Resequence (IOS):
ip access-list resequence LAN_OUT 10 10
!
! Insert at a specific line:
ip access-list extended LAN_OUT
 15 permit tcp any any eq 8080
!
show access-lists LAN_OUT
# Policy ID is NOT evaluation order.
# Order is the list order. Move it:

config firewall policy
    move 12 before 5
    move 12 after 5
    move 12 before 1
end

# See the real order:
show firewall policy | grep -e "edit" -e "set name"

Field note: Read that first line again. The policy ID is a database key, not a sequence number. Policy 47 can sit above policy 3. This is the single most dangerous assumption a Cisco engineer brings to a FortiGate: you cannot look at the IDs and know the order. Use the GUI list or the CLI order, and always “move” explicitly.

6.5 Deny, log, and the implicit rule
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip access-list extended LAN_OUT
 deny ip 10.1.1.0 0.0.0.255 host 10.9.9.9 log
 permit ip any any
!
! Implicit "deny ip any any" at the end
! of every ACL. Not logged by default.
config firewall policy
    edit 0
        set name "Block-Quarantine"
        set srcintf "port2"
        set dstintf "port1"
        set action deny
        set srcaddr "QUARANTINE_HOSTS"
        set dstaddr "all"
        set schedule "always"
        set service "ALL"
        set logtraffic all
    next
end

# Log the implicit deny (policy 0):
config system settings
    set fwpolicy-implicit-log enable
end

Field note: Turn on fwpolicy-implicit-log in every deployment. It is off by default, and without it your “why is this blocked” investigations are blind. It is the equivalent of adding “deny ip any any log” to the bottom of every ACL, except you only type it once.

6.6 Testing what a policy will do
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! ASA packet-tracer:
packet-tracer input inside tcp \
  10.1.1.10 1234 8.8.8.8 443 detailed
!
show access-list LAN_OUT
show conn address 10.1.1.10
# Which policy will match?
diagnose firewall iprope lookup \
  10.1.1.10 1234 8.8.8.8 443 6 port2

# Hit counters per policy:
diagnose firewall iprope show 100004 5

# Live sessions:
diagnose sys session filter src 10.1.1.10
diagnose sys session list
diagnose sys session clear

Field note: “diagnose firewall iprope lookup” is the FortiOS packet-tracer, and it is the fastest way to answer “which rule is catching this”. The argument order is srcip srcport dstip dstport protocol interface, where protocol is the IP protocol number (6 for TCP, 17 for UDP, 1 for ICMP). For a full trace of what happens to a packet, see the debug flow in section 12.

 6.7 Finding what references an object
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! No native equivalent.
! You grep the config and hope.
show running-config | include LAN_NET
diagnose sys checkused firewall.address.name LAN_NET
diagnose sys checkused system.interface.name port3
diagnose sys checkused firewall.vip.name WEB_VIP

Field note: This has no Cisco equivalent and it is genuinely great. When FortiOS refuses to let you delete something because it is “in use”, this tells you exactly which policy, route, or VPN is holding the reference.

7. NAT

FortiOS gives you two NAT modes. Policy NAT (the default) puts NAT inside the firewall policy. Central NAT (opt-in) gives you a separate NAT table that looks much more like the ASA model. Pick one per VDOM. You cannot mix them.

7.1 Outbound PAT (interface overload)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface GigabitEthernet0/0
 ip nat outside
!
interface GigabitEthernet0/2
 ip nat inside
!
ip access-list standard NAT_ACL
 permit 10.1.1.0 0.0.0.255
!
ip nat inside source list NAT_ACL \
  interface GigabitEthernet0/0 overload
config firewall policy
    edit 5
        set nat enable
    next
end

# That is the whole thing. It PATs to the
# IP of the dstintf. No inside/outside
# tagging. No NAT ACL. No pool.

Field note: One checkbox. No “ip nat inside” on interfaces, no NAT ACL, no direction tagging. The policy already knows the source, the destination, and the egress interface, so it has everything it needs.

7.2 Outbound NAT to a pool
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip nat pool PUBLIC_POOL 203.0.113.10 \
  203.0.113.20 netmask 255.255.255.0
!
ip nat inside source list NAT_ACL \
  pool PUBLIC_POOL overload
config firewall ippool
    edit "PUBLIC_POOL"
        set type overload
        set startip 203.0.113.10
        set endip 203.0.113.20
    next
end

config firewall policy
    edit 5
        set nat enable
        set ippool enable
        set poolname "PUBLIC_POOL"
    next
end

Field note: IP pool types: overload (PAT to a range), one-to-one (no PAT), fixed-port-range, port-block-allocation (for CGNAT and log reduction). If the pool addresses are not on the egress interface subnet, you need the upstream router to route them to you, or proxy-arp via “set arp-reply enable”.

7.3 Inbound static NAT with port forwarding
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip nat inside source static tcp \
  10.1.1.10 443 203.0.113.10 443 extendable
!
! ASA:
object network WEB_SRV
 host 10.1.1.10
 nat (inside,outside) static 203.0.113.10 \
   service tcp 443 443
config firewall vip
    edit "VIP-WEB-443"
        set extip 203.0.113.10
        set extintf "port1"
        set portforward enable
        set mappedip "10.1.1.10"
        set protocol tcp
        set extport 443
        set mappedport 443
    next
end

config firewall policy
    edit 0
        set name "Inbound-Web"
        set srcintf "port1"
        set dstintf "port2"
        set action accept
        set srcaddr "all"
        set dstaddr "VIP-WEB-443"
        set schedule "always"
        set service "HTTPS"
        set logtraffic all
    next
end

Field note: The trap: in the inbound policy, dstaddr is the VIP object, not the internal server address. FortiOS does the destination NAT before the policy lookup on the real address, so you reference the VIP. Also, a VIP with a public extip auto-answers ARP for that address unless you set arp-reply disable. That is usually what you want and occasionally a disaster if the address belongs to something else.

7.4 One-to-one static NAT (all ports)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip nat inside source static \
  10.1.1.10 203.0.113.10
config firewall vip
    edit "VIP-SRV-1TO1"
        set extip 203.0.113.10
        set extintf "port1"
        set mappedip "10.1.1.10"
    next
end

# No portforward = all ports.

Field note: Leave portforward disabled and the VIP is a full one-to-one static NAT. Bidirectional: the VIP handles inbound. For the outbound half to use the same address, either enable “set nat-source-vip enable” on the VIP or write an outbound policy with a matching IP pool.

7.5 NAT exemption / no-NAT
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! IOS: deny the traffic in the NAT ACL
ip access-list extended NAT_ACL
 deny ip 10.1.1.0 0.0.0.255 10.2.2.0 0.0.0.255
 permit ip 10.1.1.0 0.0.0.255 any
!
! ASA identity NAT:
nat (inside,outside) source static LAN LAN \
  destination static REMOTE REMOTE no-proxy-arp
# Just do not enable NAT on the policy.

config firewall policy
    edit 0
        set name "LAN-to-VPN-Remote"
        set srcintf "port2"
        set dstintf "to-branch"
        set action accept
        set srcaddr "LAN_NET"
        set dstaddr "REMOTE_NET"
        set schedule "always"
        set service "ALL"
        # set nat enable  <-- omitted
    next
end

Field note: This is where policy NAT shines. There is no NAT exemption because there is no global NAT. Each policy decides for itself. Order the VPN policy above the internet policy and you are done. No twice-NAT ordering puzzles.

7.6 Central NAT (the ASA-style option)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! ASA twice NAT / manual NAT
nat (inside,outside) source dynamic \
  LAN_NET interface
!
show nat
show xlate
config system settings
    set central-nat enable
end

config firewall central-snat-map
    edit 1
        set orig-addr "LAN_NET"
        set srcintf "port2"
        set dst-addr "all"
        set dstintf "port1"
        set nat enable
        set nat-ippool "PUBLIC_POOL"
    next
end

# DNAT moves to:
config firewall central-dnat  # (via VIPs)

Field note: Enabling central NAT strips the nat setting out of every existing policy. It is a one-way door in practice: turn it on during the build, not during a maintenance window on a live box. Use it when you need NAT decisions independent of policy decisions. Otherwise policy NAT is simpler and it is what the documentation assumes.

7.7 Verifying NAT
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show ip nat translations
show ip nat statistics
clear ip nat translation *
!
! ASA:
show xlate
show conn
diagnose sys session filter src 10.1.1.10
diagnose sys session list

# Look for these lines in the output:
#   hook=post dir=org act=snat
#   hook=pre  dir=org act=dnat

diagnose firewall ippool list
diagnose firewall ippool-all list
diagnose sys session clear

Field note: There is no separate translation table to display. NAT state lives in the session table. In “diagnose sys session list”, the “hook=post act=snat” line shows source NAT and “hook=pre act=dnat” shows destination NAT, with the pre and post addresses right there. Always set a filter first or you will dump a million sessions to your console.

8. Static Routing
8.1 Default and static routes
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip route 0.0.0.0 0.0.0.0 203.0.113.1
ip route 10.2.0.0 255.255.0.0 10.1.1.2
ip route 10.3.0.0 255.255.0.0 10.1.1.2 200
ip route 10.9.0.0 255.255.0.0 Null0
!
ip route 0.0.0.0 0.0.0.0 203.0.113.1 track 1
config router static
    edit 1
        set gateway 203.0.113.1
        set device "port1"
    next
    edit 2
        set dst 10.2.0.0 255.255.0.0
        set gateway 10.1.1.2
        set device "port2"
    next
    edit 3
        set dst 10.3.0.0 255.255.0.0
        set gateway 10.1.1.2
        set device "port2"
        set distance 200
    next
    edit 4
        set dst 10.9.0.0 255.255.0.0
        set blackhole enable
    next
end

Field note: The “set device” is mandatory and it is the thing Cisco engineers forget. A FortiOS static route is interface-plus-gateway, always. Also: the sequence number in “edit 1” is just an ID, it has nothing to do with preference. Distance and priority do that.

8.2 Distance vs priority (a FortiOS-only concept)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! Cisco has only administrative distance.
ip route 0.0.0.0 0.0.0.0 203.0.113.1
ip route 0.0.0.0 0.0.0.0 198.51.100.1 10
!
! Equal distance = ECMP
maximum-paths 4
config router static
    edit 1
        set gateway 203.0.113.1
        set device "port1"
        set distance 10
        set priority 1
    next
    edit 2
        set gateway 198.51.100.1
        set device "port3"
        set distance 10
        set priority 10
    next
end

Field note: This is the FortiOS concept with no Cisco analog, and it matters. Distance decides which route enters the routing table. Priority is a tiebreaker among routes that already made it in with the same distance: both routes are in the table, but only the lower priority is used for new sessions. Different distances give you a floating static. Same distance plus different priorities gives you a primary and a hot standby that is already installed. Same distance plus same priority gives you ECMP.

8.3 Link monitoring / route tracking
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip sla 1
 icmp-echo 8.8.8.8 source-interface Gi0/0
 frequency 5
ip sla schedule 1 life forever start-time now
!
track 1 ip sla 1 reachability
!
ip route 0.0.0.0 0.0.0.0 203.0.113.1 track 1
config system link-monitor
    edit "WAN1-MON"
        set srcintf "port1"
        set server "8.8.8.8" "1.1.1.1"
        set protocol ping
        set gateway-ip 203.0.113.1
        set interval 500
        set failtime 5
        set recoverytime 5
        set update-cascade-interface enable
        set update-static-route enable
        set status enable
    next
end

Field note: Link monitor is IP SLA plus track plus the route statement, all in one object. “set update-static-route enable” pulls every static route out of the table when the monitor fails, which is exactly what “track” does on IOS. Note interval is in milliseconds on 7.x (it was seconds on 6.x), so 500 means half a second and not eight minutes.

8.4 Policy-based routing
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
access-list 100 permit ip 10.1.1.0 0.0.0.255 any
!
route-map PBR permit 10
 match ip address 100
 set ip next-hop 10.1.1.2
!
interface GigabitEthernet0/2
 ip policy route-map PBR
!
show route-map PBR
config router policy
    edit 1
        set input-device "port2"
        set src "10.1.1.0/24"
        set dst "0.0.0.0/0"
        set protocol 6
        set start-port 443
        set end-port 443
        set gateway 10.1.1.2
        set output-device "port3"
    next
end

diagnose firewall proute list

Field note: Policy routes are evaluated BEFORE the routing table, in ID order, and they win. If a customer swears their static route is being ignored, check “diagnose firewall proute list” before you check anything else. Also, “set action deny” on a policy route makes it an exemption, sending that traffic back to normal routing.

8.5 Reading the routing table
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show ip route
show ip route 8.8.8.8
show ip route static
show ip route bgp
show ip cef 8.8.8.8
show ip route summary
get router info routing-table all
get router info routing-table details 8.8.8.8
get router info routing-table static
get router info routing-table bgp
get router info routing-table database

# The kernel FIB (what actually forwards):
diagnose ip route list
diagnose firewall proute list

Field note: “get router info routing-table all” is your “show ip route”. “routing-table database” is the RIB including routes that lost, which is your “show ip route database”. And remember: even a perfect routing table means nothing without a firewall policy. The route says where, the policy says whether.

8.6 VRF
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
vrf definition GUEST
 rd 65001:10
 address-family ipv4
 exit-address-family
!
interface Vlan20
 vrf forwarding GUEST
 ip address 192.168.20.1 255.255.255.0
!
ip route vrf GUEST 0.0.0.0 0.0.0.0 192.168.20.254
show ip route vrf GUEST
config system interface
    edit "VLAN20-GUEST"
        set vrf 10
        set ip 192.168.20.1 255.255.255.0
    next
end

config router static
    edit 5
        set gateway 192.168.20.254
        set device "VLAN20-GUEST"
    next
end

get router info routing-table all vrf 10

Field note: FortiOS VRFs are numbered (0 to 251 on 7.x), not named, and VRF 0 is the default. They separate routing only, not policy or objects. For real separation with independent admins, policies, and logging, you want a VDOM, which is the ASA security context model.

9. OSPF

FortiOS OSPF is a Quagga derivative, so the show output will look familiar to anyone who has used Zebra. Two things change: areas are always dotted decimal, and network statements use subnet masks instead of wildcard masks.

9.1 Basic OSPF process
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
router ospf 1
 router-id 10.255.255.1
 network 10.1.1.0 0.0.0.255 area 0
 network 10.10.10.0 0.0.0.255 area 0
 passive-interface default
 no passive-interface GigabitEthernet0/1
 log-adjacency-changes
!
config router ospf
    set router-id 10.255.255.1
    config area
        edit 0.0.0.0
        next
    end
    config network
        edit 1
            set prefix 10.1.1.0 255.255.255.0
            set area 0.0.0.0
        next
        edit 2
            set prefix 10.10.10.0 255.255.255.0
            set area 0.0.0.0
        next
    end
    set passive-interface "VLAN10-USERS" \
                          "VLAN20-VOICE"
end

Field note: Three things to burn in. One: there is no process ID, there is one OSPF instance per VDOM. Two: “area 0” must be typed as “0.0.0.0”, every time. Three, and this is the painful one: there is no “passive-interface default”. You must enumerate every passive interface by name, and “set passive-interface” is a replace, not an append. Adding VLAN30 means retyping VLAN10 and VLAN20 as well. On a box with 40 VLANs this is genuinely awful, and no, there is no shortcut.

9.2 Per-interface OSPF settings
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface GigabitEthernet0/1
 ip ospf cost 10
 ip ospf priority 100
 ip ospf hello-interval 5
 ip ospf dead-interval 20
 ip ospf network point-to-point
 ip ospf mtu-ignore
!
config router ospf
    config ospf-interface
        edit "to-core"
            set interface "port3"
            set cost 10
            set priority 100
            set hello-interval 5
            set dead-interval 20
            set network-type point-to-point
            set mtu-ignore enable
            set status enable
        next
    end
end

Field note: The ospf-interface object has a name of its own (“to-core”) and then points at the real interface with “set interface”. That indirection surprises people. The name is just a label, so make it meaningful. Setting network-type point-to-point on your transit links is as good an idea here as it is on IOS.

9.3 OSPF authentication
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface GigabitEthernet0/1
 ip ospf authentication message-digest
 ip ospf message-digest-key 1 md5 Sh4redK3y
!
! Or area-wide:
router ospf 1
 area 0 authentication message-digest
config router ospf
    config ospf-interface
        edit "to-core"
            set interface "port3"
            set authentication md5
            config md5-keys
                edit 1
                    set key-string Sh4redK3y
                next
            end
        next
    end
end

Field note: Options are none, text, md5, and (on 7.2+) message-digest with SHA. Set it on the ospf-interface, not the area, if you want per-link control.

9.4 Stub, NSSA, and summarization
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
router ospf 1
 area 1 stub
 area 1 stub no-summary
 area 2 nssa
 area 1 range 10.1.0.0 255.255.0.0
 summary-address 172.16.0.0 255.255.0.0
!
config router ospf
    config area
        edit 0.0.0.1
            set type stub
            set no-summary enable
            config range
                edit 1
                    set prefix 10.1.0.0 255.255.0.0
                    set advertise enable
                next
            end
        next
        edit 0.0.0.2
            set type nssa
            set nssa-translator-role candidate
        next
    end
    config summary-address
        edit 1
            set prefix 172.16.0.0 255.255.0.0
        next
    end
end

Field note: Same semantics as IOS. “set no-summary enable” on a stub area makes it totally stubby. summary-address is for external routes on an ASBR, area range is for inter-area on an ABR, exactly as you expect.

9.5 Redistribution and default origination
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
router ospf 1
 redistribute connected subnets metric 20
 redistribute static subnets route-map STATIC_IN
 redistribute bgp 65001 subnets
 default-information originate always metric 10
!
config router ospf
    config redistribute "connected"
        set status enable
        set metric 20
    end
    config redistribute "static"
        set status enable
        set routemap "STATIC_IN"
    end
    config redistribute "bgp"
        set status enable
    end
    set default-information-originate always
    set default-information-metric 10
    set default-information-metric-type 2
end

Field note: Redistribute blocks are fixed names in quotes: “connected”, “static”, “rip”, “bgp”, “isis”. You cannot create new ones, you enable the ones that exist. Note there is no “subnets” keyword because FortiOS never had the classful behavior that made it necessary.

9.6 OSPF verification and debugging
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show ip ospf neighbor
show ip ospf interface brief
show ip ospf database
show ip ospf database router 10.255.255.1
show ip protocols
!
debug ip ospf adj
debug ip ospf events
undebug all
get router info ospf neighbor
get router info ospf interface
get router info ospf database brief
get router info ospf database router
get router info ospf status

diagnose ip router ospf level info
diagnose ip router ospf all enable
diagnose debug enable

# Turn it off:
diagnose ip router ospf all disable
diagnose debug disable
diagnose debug reset

Field note: The debug pattern is always three steps: pick the module, set the level, then “diagnose debug enable” to actually send output to your terminal. Forgetting the third step is the most common reason people think FortiOS debugs are broken. And always “diagnose debug reset” when you are done, because those filters are sticky and will confuse the next engineer.

10. BGP

If you know IOS BGP you know FortiOS BGP. Same attributes, same path selection, same route maps. The syntax is flatter: there is no address-family submode for basic IPv4 unicast, and neighbor settings live inside the neighbor object instead of being repeated on twelve separate lines.

10.1 Basic eBGP peering
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
router bgp 65001
 bgp router-id 10.255.255.1
 bgp log-neighbor-changes
 neighbor 203.0.113.1 remote-as 65002
 neighbor 203.0.113.1 description ISP-A
 neighbor 203.0.113.1 password Sh4redK3y
 !
 address-family ipv4 unicast
  neighbor 203.0.113.1 activate
  neighbor 203.0.113.1 soft-reconfiguration inbound
  neighbor 203.0.113.1 route-map ISP-A-IN in
  neighbor 203.0.113.1 route-map ISP-A-OUT out
  network 198.51.100.0 mask 255.255.255.0
 exit-address-family
!
config router bgp
    set as 65001
    set router-id 10.255.255.1
    set log-neighbour-changes enable
    set ebgp-multipath enable
    config neighbor
        edit "203.0.113.1"
            set remote-as 65002
            set description "ISP-A"
            set password Sh4redK3y
            set soft-reconfiguration enable
            set route-map-in "ISP-A-IN"
            set route-map-out "ISP-A-OUT"
        next
    end
    config network
        edit 1
            set prefix 198.51.100.0 255.255.255.0
        next
    end
end

Field note: Note “log-neighbour-changes” with the British spelling. Fortinet is inconsistent about this and it will fail your tab completion. Also note there is no “activate”: IPv4 unicast is on by default. IPv6 peers go in “config neighbor” too but use “config network6” and route-map6 variants.

10.2 iBGP with loopbacks
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
router bgp 65001
 neighbor 10.255.255.2 remote-as 65001
 neighbor 10.255.255.2 update-source Loopback0
 neighbor 10.255.255.2 next-hop-self
 neighbor 10.255.255.2 route-reflector-client
!
config router bgp
    config neighbor
        edit "10.255.255.2"
            set remote-as 65001
            set update-source "lb0"
            set next-hop-self enable
            set route-reflector-client enable
            set soft-reconfiguration enable
        next
    end
end

Field note: Same rules apply: you need a route to the loopback (IGP or static), and the loopback needs to permit the BGP session. On FortiGate that last part is real, because you may need a local-in policy or the right allowaccess if you have hardened the box.

10.3 eBGP multihop
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
router bgp 65001
 neighbor 10.255.255.40 remote-as 65002
 neighbor 10.255.255.40 ebgp-multihop 2
 neighbor 10.255.255.40 update-source Loopback0
 neighbor 10.255.255.40 disable-connected-check
!
config router bgp
    config neighbor
        edit "10.255.255.40"
            set remote-as 65002
            set ebgp-enforce-multihop enable
            set ebgp-multihop-ttl 2
            set update-source "lb0"
        next
    end
end

Field note: Enabling ebgp-enforce-multihop without setting a TTL defaults you to 255, which works but is sloppy. Set the TTL to the real hop count.

10.4 Timers, dampening, and prefix limits
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
router bgp 65001
 timers bgp 30 90
 neighbor 203.0.113.1 timers 10 30
 neighbor 203.0.113.1 maximum-prefix 500 80 \
   warning-only
 bgp graceful-restart
 neighbor 203.0.113.1 fall-over bfd
!
config router bgp
    set keepalive-timer 30
    set holdtime-timer 90
    set graceful-restart enable
    config neighbor
        edit "203.0.113.1"
            set keep-alive-timer 10
            set holdtime-timer 30
            set maximum-prefix 500
            set maximum-prefix-threshold 80
            set maximum-prefix-warning-only enable
            set bfd enable
        next
    end
end

Field note: Watch the naming: the global timer is “keepalive-timer” and the per-neighbor one is “keep-alive-timer” with a hyphen. This is not a typo in this document. It is a typo in FortiOS, and it has survived for a decade.

10.5 Prefix lists
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip prefix-list ISP-IN seq 5 permit 0.0.0.0/0
ip prefix-list ISP-IN seq 10 deny 10.0.0.0/8 le 32
ip prefix-list ISP-IN seq 15 permit 0.0.0.0/0 le 24
!
show ip prefix-list
config router prefix-list
    edit "ISP-IN"
        config rule
            edit 1
                set prefix 0.0.0.0 0.0.0.0
                set action permit
                unset ge
                unset le
            next
            edit 2
                set prefix 10.0.0.0 255.0.0.0
                set action deny
                set le 32
            next
            edit 3
                set prefix 0.0.0.0 0.0.0.0
                set action permit
                set le 24
            next
        end
    next
end

Field note: The “unset ge / unset le” on an exact-match default route entry is not optional decoration. If ge or le are left at their inherited values you will match more than you meant to. This is the number one cause of “my default-route-only filter is leaking the full table” tickets.

10.6 Route maps
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip as-path access-list 1 permit ^65002$
!
route-map ISP-A-IN permit 10
 match ip address prefix-list ISP-IN
 set local-preference 200
 set community 65001:100 additive
!
route-map ISP-A-IN permit 20
 match as-path 1
 set weight 100
!
route-map ISP-A-OUT permit 10
 match ip address prefix-list MY-PREFIXES
 set as-path prepend 65001 65001
!
config router aspath-list
    edit "AS-65002"
        config rule
            edit 1
                set action permit
                set regexp "^65002$"
            next
        end
    next
end

config router route-map
    edit "ISP-A-IN"
        config rule
            edit 1
                set action permit
                set match-ip-address "ISP-IN"
                set set-local-preference 200
                set set-community "65001:100"
                set set-community-additive enable
            next
            edit 2
                set action permit
                set match-as-path "AS-65002"
                set set-weight 100
            next
        end
    next
end

Field note: The pattern is mechanical: Cisco “match X” becomes “set match-X”, and Cisco “set Y” becomes “set set-Y”. Yes, “set set-local-preference” reads like a stutter. You get used to it. Rule IDs are your sequence numbers, and like Cisco they are evaluated in order with an implicit deny at the end.

10.7 BGP verification
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show ip bgp summary
show ip bgp
show ip bgp neighbors 203.0.113.1
show ip bgp neighbors 203.0.113.1 received-routes
show ip bgp neighbors 203.0.113.1 advertised-routes
show ip bgp 8.8.8.0/24
!
clear ip bgp * soft
clear ip bgp 203.0.113.1 soft in
get router info bgp summary
get router info bgp network
get router info bgp neighbors 203.0.113.1
get router info bgp neighbors 203.0.113.1 \
  received-routes
get router info bgp neighbors 203.0.113.1 \
  advertised-routes
get router info bgp network 8.8.8.0/24

execute router clear bgp all soft in
execute router clear bgp ip 203.0.113.1 soft in

diagnose ip router bgp level info
diagnose ip router bgp all enable
diagnose debug enable

Field note: “received-routes” only works if you enabled “set soft-reconfiguration enable” on the neighbor, exactly like IOS. Turn it on for every peer where memory allows. The 90 seconds it costs you at build time saves an hour during an outage.

11. IPsec VPN

FortiOS route-based IPsec is Cisco VTI. If you build tunnels with VTIs today, you will be at home immediately. If you still build crypto maps, this is a good moment to stop, because FortiOS policy-based VPN is a legacy mode that Fortinet is steadily deprecating.

11.1 Site-to-site IKEv2 (route-based / VTI)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
crypto ikev2 proposal PROP-AES256
 encryption aes-cbc-256
 integrity sha256
 group 14
!
crypto ikev2 policy POL-1
 proposal PROP-AES256
!
crypto ikev2 keyring KR-1
 peer BRANCH
  address 198.51.100.2
  pre-shared-key S3cretPSK
!
crypto ikev2 profile PROF-1
 match identity remote address 198.51.100.2 \
   255.255.255.255
 authentication local pre-share
 authentication remote pre-share
 keyring local KR-1
 lifetime 86400
 dpd 10 3 periodic
!
crypto ipsec transform-set TS-1 \
  esp-aes 256 esp-sha256-hmac
 mode tunnel
!
crypto ipsec profile IPSEC-PROF-1
 set transform-set TS-1
 set pfs group14
 set ikev2-profile PROF-1
!
interface Tunnel0
 description To-Branch
 ip address 169.254.1.1 255.255.255.252
 tunnel source GigabitEthernet0/0
 tunnel mode ipsec ipv4
 tunnel destination 198.51.100.2
 tunnel protection ipsec profile IPSEC-PROF-1
!
ip route 10.2.0.0 255.255.0.0 Tunnel0
config vpn ipsec phase1-interface
    edit "to-branch"
        set interface "port1"
        set ike-version 2
        set peertype any
        set net-device disable
        set proposal aes256-sha256
        set dhgrp 14
        set remote-gw 198.51.100.2
        set psksecret S3cretPSK
        set keylife 86400
        set dpd on-idle
        set dpd-retryinterval 10
    next
end

config vpn ipsec phase2-interface
    edit "to-branch-p2"
        set phase1name "to-branch"
        set proposal aes256-sha256
        set dhgrp 14
        set keylifeseconds 3600
        set src-subnet 0.0.0.0 0.0.0.0
        set dst-subnet 0.0.0.0 0.0.0.0
    next
end

# The tunnel is now an interface.
config system interface
    edit "to-branch"
        set ip 169.254.1.1 255.255.255.255
        set remote-ip 169.254.1.2 255.255.255.252
        set allowaccess ping
    next
end

config router static
    edit 10
        set dst 10.2.0.0 255.255.0.0
        set device "to-branch"
    next
end

Field note: Four things worth flagging. One: phase1-interface (not phase1) is what makes it route-based, and it creates a real interface named after the phase1. Two: with src-subnet and dst-subnet at 0.0.0.0/0, the selectors are wide open and routing decides what crosses, which is exactly VTI behavior. Three: the tunnel IP uses a /32 with a separate “set remote-ip”, which is odd but correct. Four, and this is the one people miss: you still need firewall policies in both directions with the tunnel as an interface. The tunnel coming up does not mean traffic flows.

11.2 The firewall policies the tunnel needs
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! With VTI, an ACL on the tunnel
! interface if you want one.
! Otherwise routing alone is enough.
interface Tunnel0
 ip access-group TUNNEL_IN in
!
config firewall policy
    edit 0
        set name "LAN-to-Branch"
        set srcintf "port2"
        set dstintf "to-branch"
        set srcaddr "LAN_NET"
        set dstaddr "BRANCH_NET"
        set action accept
        set schedule "always"
        set service "ALL"
        set logtraffic all
    next
    edit 0
        set name "Branch-to-LAN"
        set srcintf "to-branch"
        set dstintf "port2"
        set srcaddr "BRANCH_NET"
        set dstaddr "LAN_NET"
        set action accept
        set schedule "always"
        set service "ALL"
        set logtraffic all
    next
end

Field note: Two policies, not one. FortiOS matches on the first packet of a session, so a session initiated from the branch needs its own policy. This is the number one reason a Cisco engineer sees an established tunnel with zero traffic and starts blaming phase 2.

11.3 Dial-up / dynamic peers
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
crypto ikev2 profile PROF-DYN
 match identity remote fqdn domain branch.example.com
 authentication local pre-share
 authentication remote pre-share
 keyring local KR-DYN
 virtual-template 1
!
interface Virtual-Template1 type tunnel
 ip unnumbered Loopback0
 tunnel mode ipsec ipv4
 tunnel protection ipsec profile IPSEC-PROF-1
!
config vpn ipsec phase1-interface
    edit "dialup-branches"
        set type dynamic
        set interface "port1"
        set ike-version 2
        set peertype one
        set peerid "branch.example.com"
        set proposal aes256-sha256
        set dhgrp 14
        set psksecret S3cretPSK
        set add-route enable
        set exchange-interface-ip enable
    next
end

Field note: “set type dynamic” is the virtual-template equivalent: one phase1 accepts many peers. “set add-route enable” installs routes learned from the peer selectors automatically. For anything at scale, this is where you stop hand-rolling and start looking at ADVPN, which is FortiOS DMVPN with a better shortcut mechanism.

11.4 VPN verification and debugging
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show crypto ikev2 sa detailed
show crypto ipsec sa peer 198.51.100.2
show crypto session detail
show interface Tunnel0
!
clear crypto ikev2 sa remote 198.51.100.2
clear crypto session remote 198.51.100.2
!
debug crypto ikev2
debug crypto ipsec
diagnose vpn ike gateway list name to-branch
diagnose vpn tunnel list name to-branch
get vpn ipsec tunnel summary
get router info routing-table all | grep to-branch

diagnose vpn ike gateway clear name to-branch
diagnose vpn tunnel down to-branch
diagnose vpn tunnel up to-branch

# The debug everyone actually uses:
diagnose debug reset
diagnose vpn ike log-filter clear
diagnose vpn ike log-filter dst-addr4 198.51.100.2
diagnose debug application ike -1
diagnose debug enable

diagnose debug disable
diagnose debug reset

Field note: Learn this five-line debug sequence and you can troubleshoot any FortiGate VPN in the field. The log-filter is essential: on a hub with 200 tunnels, “diagnose debug application ike -1” without a filter will melt your session and possibly the CPU. Filter first, every time.

12. High Availability
12.1 First-hop redundancy (HSRP / VRRP)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface Vlan10
 ip address 10.10.10.2 255.255.255.0
 standby version 2
 standby 10 ip 10.10.10.1
 standby 10 priority 110
 standby 10 preempt delay minimum 60
 standby 10 authentication md5 key-string S3cret
 standby 10 track 1 decrement 20
!
show standby brief
config system interface
    edit "VLAN10-USERS"
        set ip 10.10.10.2 255.255.255.0
        set vrrp-virtual-mac enable
        config vrrp
            edit 10
                set vrgrp 1
                set vrip 10.10.10.1
                set priority 110
                set adv-interval 1
                set preempt enable
                set start-time 60
                set status enable
            next
        end
    next
end

diagnose sys vrrp list

Field note: FortiGate does VRRP, not HSRP, so if you are replacing one of a pair of Cisco routers you have to convert both ends. VRRP on FortiGate is mostly used to interoperate with non-Fortinet gear. Between two FortiGates you want FGCP instead, which is far better.

12.2 Chassis / cluster HA (FGCP)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
! ASA active/standby failover
failover lan unit primary
failover lan interface FO GigabitEthernet0/3
failover link STATE GigabitEthernet0/4
failover interface ip FO 10.0.0.1 255.255.255.252 \
  standby 10.0.0.2
failover key S3cretK3y
failover
!
show failover
config system ha
    set group-name "FGT-EDGE-CLUSTER"
    set group-id 10
    set mode a-p
    set password S3cretK3y
    set hbdev "port9" 50 "port10" 50
    set session-pickup enable
    set session-pickup-connectionless enable
    set ha-mgmt-status enable
    config ha-mgmt-interfaces
        edit 1
            set interface "mgmt"
            set gateway 10.99.99.1
        next
    end
    set override disable
    set priority 200
    set monitor "port1" "port2"
end

Field note: Key differences from ASA failover. “set override disable” is the default and it means the cluster does NOT preempt, which surprises everyone: after a failback the old secondary stays primary. That is usually correct behavior and occasionally not what the customer wants. “set monitor” is your interface tracking. And “set priority” only matters if override is enabled or if you are comparing at boot time, because uptime wins first by default.

12.3 HA operations
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show failover
show failover history
failover active
failover reload-standby
!
! Console into the standby:
! (separate cable, separate box)
get system ha status
diagnose sys ha status
diagnose sys ha checksum show
diagnose sys ha dump-by vcluster

# Force a failover:
execute ha failover set 1
execute ha failover unset 1

# Jump to the other member from here:
execute ha manage 1 admin
execute ha manage ?

Field note: “execute ha manage 1 admin” is the command that makes cluster life bearable: it SSHes to the other member over the heartbeat link from your existing session. And “diagnose sys ha checksum show” is your first stop when the cluster is out of sync, because a checksum mismatch tells you exactly which config tree diverged.

13. DHCP and System Services
13.1 DHCP server
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
ip dhcp excluded-address 10.10.10.1 10.10.10.20
!
ip dhcp pool USERS
 network 10.10.10.0 255.255.255.0
 default-router 10.10.10.1
 dns-server 10.1.1.53 10.1.1.54
 domain-name corp.example.com
 lease 0 8 0
 option 150 ip 10.1.1.60
!
show ip dhcp binding
config system dhcp server
    edit 1
        set interface "VLAN10-USERS"
        set default-gateway 10.10.10.1
        set netmask 255.255.255.0
        set domain "corp.example.com"
        set dns-service specify
        set dns-server1 10.1.1.53
        set dns-server2 10.1.1.54
        set lease-time 28800
        config ip-range
            edit 1
                set start-ip 10.10.10.21
                set end-ip 10.10.10.200
            next
        end
        config options
            edit 1
                set code 150
                set type ip
                set ip "10.1.1.60"
            next
        end
    next
end

execute dhcp lease-list

Field note: There is no excluded-address concept because you define the range you want to hand out instead of the subnet minus exclusions. Lease time is in seconds, not the days/hours/minutes triplet. Reservations go in “config reserved-address” inside the same server object.

13.2 DHCP relay
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
interface Vlan10
 ip helper-address 10.1.1.50
 ip helper-address 10.1.1.51
!
config system interface
    edit "VLAN10-USERS"
        set dhcp-relay-service enable
        set dhcp-relay-type regular
        set dhcp-relay-ip "10.1.1.50" "10.1.1.51"
        set dhcp-relay-link-selection 0.0.0.0
    next
end

Field note: Mind the quoting: dhcp-relay-ip takes quoted, space-separated addresses. And remember that relayed DHCP is traffic through the box, so the relay needs a route to the server and, if the server is across a VPN, a policy.

13.3 NTP, DNS, hostname, and timezone
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
hostname R1-EDGE
!
clock timezone EST -5 0
clock summer-time EDT recurring
!
ntp server 10.1.1.5 prefer
ntp server 10.1.1.6
ntp source Loopback0
!
ip name-server 10.1.1.53 10.1.1.54
ip domain-name corp.example.com
!
show ntp associations
show clock
config system global
    set hostname "FGT-EDGE-01"
    set timezone "America/New_York"
end

config system ntp
    set ntpsync enable
    set type custom
    set syncinterval 60
    set source-ip 10.255.255.1
    config ntpserver
        edit 1
            set server "10.1.1.5"
        next
        edit 2
            set server "10.1.1.6"
        next
    end
end

config system dns
    set primary 10.1.1.53
    set secondary 10.1.1.54
    set domain "corp.example.com"
end

diagnose sys ntp status
execute time
execute date

Field note: On FortiOS 7.4.4 and later the timezone accepts names like “America/New_York”. On older builds it is a numeric ID and you find yours with “set timezone ?”. Daylight saving is handled by the zone database, so there is no summer-time command.

13.4 Syslog
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
logging host 10.1.1.60
logging trap informational
logging facility local7
logging source-interface Loopback0
logging buffered 64000
service timestamps log datetime msec localtime
!
show logging
config log syslogd setting
    set status enable
    set server "10.1.1.60"
    set port 514
    set mode udp
    set facility local7
    set source-ip "10.255.255.1"
    set format rfc5424
end

config log syslogd filter
    set severity information
    set forward-traffic enable
    set local-traffic enable
end

config log memory setting
    set status enable
end

execute log filter category traffic
execute log filter device memory
execute log display

Field note: You get four syslog server slots (syslogd, syslogd2, syslogd3, syslogd4). The filter is a separate tree from the setting, and forgetting to enable forward-traffic in the filter is why your SIEM sees system events but no firewall logs. Also worth knowing: logging is only as good as the “set logtraffic all” on your policies.

13.5 SNMP
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
snmp-server community R3ad0nly RO 10
snmp-server location "MIA-DC1 Rack 4"
snmp-server contact noc@example.com
snmp-server host 10.1.1.61 version 2c R3ad0nly
snmp-server enable traps
!
snmp-server group GRP v3 priv
snmp-server user monitor GRP v3 auth sha AuthP4ss \
  priv aes 128 PrivP4ss
config system snmp sysinfo
    set status enable
    set location "MIA-DC1 Rack 4"
    set contact-info "noc@example.com"
end

config system snmp community
    edit 1
        set name "R3ad0nly"
        config hosts
            edit 1
                set ip 10.1.1.61 255.255.255.255
            next
        end
        set query-v2c-status enable
        set trap-v2c-status enable
    next
end

config system snmp user
    edit "monitor"
        set security-level auth-priv
        set auth-proto sha256
        set auth-pwd AuthP4ss
        set priv-proto aes128
        set priv-pwd PrivP4ss
        set notify-hosts 10.1.1.61
    next
end

# And do not forget:
config system interface
    edit "port2"
        set allowaccess ping https ssh snmp
    next
end

Field note: That last block is the one everybody forgets. SNMP is configured, the community is right, the ACL is right, and it still does not poll, because “snmp” is not in the interface allowaccess list. Check it first, always.

14. Troubleshooting: The Commands That Matter at 2 a.m.

If you read only one section of this guide, read this one. FortiOS troubleshooting is genuinely more powerful than IOS once you know the four commands below, and genuinely opaque until you do.

14.1 Packet capture
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
monitor capture CAP interface Gi0/1 both
monitor capture CAP match ipv4 host 10.1.1.10 any
monitor capture CAP start
monitor capture CAP stop
show monitor capture CAP buffer brief
monitor capture CAP export flash:cap.pcap
!
! ASA:
capture CAP interface inside match tcp \
  host 10.1.1.10 any eq 443
# Syntax:
# diagnose sniffer packet <intf> <filter>
#        <verbose> <count> <timestamp>

diagnose sniffer packet port1 \
  "host 10.1.1.10 and port 443" 4 0 a

# Verbose levels:
#  1 = header only
#  3 = header + payload
#  4 = header + interface name
#  6 = header + payload + interface name
# Count 0 = until Ctrl+C
# Timestamp a = absolute UTC

# Every interface:
diagnose sniffer packet any "icmp" 4 0 a

# Real pcap for Wireshark:
diagnose sniffer packet any \
  "host 10.1.1.10" 6 0 a

Field note: The filter is standard tcpdump/BPF syntax, so everything you know from tcpdump works verbatim. Verbose 4 is the sweet spot for “which interface did this arrive on and where did it go”. For a real pcap, use verbose 6 and pipe the output through the fgt2eth.pl script Fortinet publishes, or just use the GUI packet capture, which hands you a .pcap directly.

14.2 Debug flow (the killer feature)
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
debug ip packet detail 100
! Dangerous. Rate-limited. Vague.
!
! ASA packet-tracer is closer:
packet-tracer input inside tcp \
  10.1.1.10 1234 8.8.8.8 443 detailed
diagnose debug reset
diagnose debug flow filter clear
diagnose debug flow filter addr 10.1.1.10
diagnose debug flow filter port 443
diagnose debug flow filter proto 6
diagnose debug flow show function-name enable
diagnose debug flow show iprope enable
diagnose debug flow trace start 100
diagnose debug enable

# ... generate the traffic ...

diagnose debug flow trace stop
diagnose debug disable
diagnose debug reset

Field note: This is the best troubleshooting tool on any firewall I have used, and it has no real IOS equivalent. It follows a live packet through the entire stack and prints, in plain English, the route it picked, the policy it matched or why it did not, the NAT it applied, and where it was dropped. “iprope_in_check() check failed on policy 0, drop” tells you the implicit deny caught it. Nothing on IOS tells you that. Learn the sequence, and always reset the filter afterward.

14.3 Sessions, ARP, and MAC
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show ip arp
clear arp-cache
show mac address-table
show ip cef
!
! ASA:
show conn address 10.1.1.10
clear conn address 10.1.1.10
get system arp
diagnose ip arp list
execute clear system arp table

diagnose sys session filter clear
diagnose sys session filter src 10.1.1.10
diagnose sys session filter dst 8.8.8.8
diagnose sys session filter dport 443
diagnose sys session list
diagnose sys session stat
diagnose sys session clear

diagnose netlink brctl name host lan-sw

Field note: Set a filter before you list, or you will dump the entire session table to your terminal. On a busy box that is minutes of scrollback and a very annoyed customer. And note that “diagnose sys session clear” clears what matches the CURRENT filter, so a stale filter from an hour ago means you clear the wrong sessions. Clear the filter when you finish.

14.4 CPU, memory, and conserve mode
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show processes cpu sorted
show processes cpu history
show processes memory sorted
show memory statistics
show platform resources
get system performance status
get system performance top
diagnose sys top 5 20
diagnose sys top-summary
diagnose hardware sysinfo memory
diagnose hardware sysinfo conserve
diagnose sys session stat

# NPU/hardware offload:
diagnose npu np6 sess-stats 0
get system npu

Field note: “diagnose hardware sysinfo conserve” has no Cisco equivalent and it is the first thing to check when a FortiGate starts behaving strangely under load. Conserve mode is the box protecting itself by dropping or passing traffic unscanned once memory crosses a threshold, and it explains a huge share of the “the firewall randomly broke” tickets.

14.5 Logs and support bundles
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
show logging
show tech-support
show tech-support | redirect tftp://10.1.1.9/tech.txt
show archive
execute log filter category traffic
execute log filter device memory
execute log filter field srcip 10.1.1.10
execute log display

execute tac report

# Everything at once for TAC:
diagnose debug report
execute backup config tftp fgt.conf 10.1.1.9

Field note: “execute tac report” runs the entire diagnostic battery Fortinet TAC will ask for. Run it before you open the ticket, not after they ask, and you will save yourself a full support cycle.

14.6 Turning debugs off
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
undebug all
no debug all
show debugging
diagnose debug disable
diagnose debug reset
diagnose debug flow trace stop
diagnose debug flow filter clear
diagnose vpn ike log-filter clear
diagnose sys session filter clear
diagnose debug info

Field note: There is no single “undebug all” that catches everything, and filters survive your SSH session. “diagnose debug reset” plus “diagnose debug disable” covers most of it, and “diagnose debug info” shows you what is still running. Leaving an IKE debug enabled on a production hub is a career-limiting move. Check before you log out.

15. Config Management and Upgrades
15.1 Backup and restore
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
copy running-config tftp://10.1.1.9/r1.cfg
copy tftp://10.1.1.9/r1.cfg running-config
copy running-config flash:backup.cfg
!
archive
 path flash:archive
 write-memory
execute backup config tftp fgt.conf 10.1.1.9
execute backup config ftp fgt.conf 10.1.1.9 user pass
execute backup config usb fgt.conf

execute restore config tftp fgt.conf 10.1.1.9

# Encrypted backup (recommended):
config system global
    set private-data-encryption enable
end

# On-box revisions:
execute revision list config
execute revision diff config 11 12
execute restore config flash 11

Field note: A plain backup does not include passwords, VPN PSKs, or certificates in a restorable form unless you either set a backup password or enable private-data-encryption. A restore of an unencrypted config onto a different serial number gives you a working config with dead VPNs. Test your restores.

15.2 Firmware upgrade
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
copy tftp://10.1.1.9/image.bin flash:
verify /md5 flash:image.bin
boot system flash:image.bin
write memory
reload
execute restore image tftp \
  FGT_60F-v7.4.4.out 10.1.1.9
# The box validates, writes, and reboots.
# One command. No boot statements.

execute restore image usb image.out

# HA cluster: upgrade is rolling and
# automatic. Just do it on the primary.
get system ha status

# Check the upgrade path first:
get system status

Field note: FortiOS has no boot system statement and no image selection. There are two firmware partitions and the box flips between them. Always check the official upgrade path before you jump versions, because FortiOS will happily let you install a build that requires an intermediate hop and then hand you a mangled config. On an HA pair, the upgrade rolls through the cluster automatically.

15.3 Factory reset and recovery
Cisco IOS / IOS-XE / ASA FortiGate / FortiOS
erase startup-config
delete flash:vlan.dat
reload
!
! ROMMON password recovery:
! confreg 0x2142
execute factoryreset
execute factoryreset2   # keeps VDOM/image
execute formatlogdisk

# Console-only recovery:
# Interrupt the boot, use the BIOS menu,
# TFTP a fresh image over the console/mgmt.
# Login: maintainer
# Password: bcpb<serial-number>
# (must be within 60s of a hard power cycle,
#  and only if "set admin-maintainer" is
#  enabled, which it is by default)

Field note: The maintainer account is the ROMMON equivalent and it is worth knowing about before you need it. It requires physical console access and a power cycle, so it is not a remote hole, but hardened environments disable it with “config system global / set admin-maintainer disable”. Do that only if you are certain about your console procedures.

16. The Gotcha List

These are the things that cost Cisco engineers real hours on their first FortiGate. None of them are in the release notes.

– There is no write memory and no unsaved state. Typing endwrites to flash. If you are used to experimenting freely and reloading out of trouble, that safety net is gone. Back up first.

set allowaccess is a replace, not an append. Typing set allowaccess ssh on the interface you are managing the box from, when it previously had ping https ssh, is how you lock yourself out of a production firewall from 1,200 miles away.

– The policy ID is not the evaluation order. Policy 47 can sit above policy 3. You cannot read the order from the IDs. Ever.

– A route is not permission. The routing table tells the box where, the policy tells it whether. Both must agree. Two interfaces in the same subnet on the same box still will not talk without a policy.

– Policy routes and SD-WAN rules beat the routing table. If your static route “is not working”, check diagnose firewall proute list before you check anything else.

– Subnet masks everywhere, never wildcard masks. A wildcard mask typed from habit will not error. It will just silently build an object for a network that does not exist.

show hides defaults. If you cannot find the setting you configured, it probably matches the default. Use show full-configuration and filter it.

– OSPF has no passive-interface default, and set passive-interface replaces the whole list. Adding one interface means retyping all of them.

– OSPF area 0 must be typed as 0.0.0.0. Always. There is no shorthand.

– Ping options are sticky. Set a source address once and every subsequent ping in that session uses it until you execute ping-options reset.

– Session filters are sticky too, and diagnose sys session clear clears whatever the current filter matches. A forgotten filter from an hour ago will clear the wrong sessions.

– An IPsec tunnel being up means nothing. You still need firewall policies in both directions. This is the number one false alarm from Cisco engineers on FortiGate VPNs.

– SNMP will not poll without snmp in the interface allowaccess list, no matter how perfect the rest of the config is.

set mtu is silently ignored unless you set mtu-override enable first.

– HA does not preempt by default (set override disable). After a failback the old secondary stays primary. That is correct, and it will still generate a ticket.

– The implicit deny is not logged by default. Turn on set fwpolicy-implicit-log enable in every build.

– Enabling central NAT strips the nat setting from every existing policy. Decide during the build, not during a change window.

– VDOMs change the command tree. If a command does not exist, check whether you are in config global or inside a VDOM. Many settings are global-only and many are VDOM-only.

– Aggregate member ports must be referenced by nothing else. diagnose sys checkused system.interface.name port3 tells you who is holding it.

– The debug output goes nowhere until you type diagnose debug enable. This is the third step people skip, and then they conclude FortiOS debugs are broken.

17. The One-Page Cheat Sheet

Print this part. Tape it to the monitor. The rest of the guide is reference; this is the muscle memory transplant.

17.1 Daily driver commands
Cisco IOS FortiOS
configure terminal config <tree>
exit / end next (close object) / end (commit tree)
Ctrl+Z with regret abort
show running-config show
show running-config all show full-configuration
show run | section router bgp show router bgp
show run | include vlan show | grep vlan
show run | section interface Gi0/1 show | grep -f port1
write memory (nothing: end saves it)
copy run tftp: execute backup config tftp <file> <ip>
reload execute reboot
show version get system status
show ip interface brief get system interface physical
show interfaces Gi0/1 diagnose hardware deviceinfo nic port1
show ip route get router info routing-table all
show ip route 8.8.8.8 get router info routing-table details 8.8.8.8
show ip arp get system arp
show ip ospf neighbor get router info ospf neighbor
show ip bgp summary get router info bgp summary
clear ip bgp * soft execute router clear bgp all soft in
show crypto ipsec sa diagnose vpn tunnel list
show crypto ikev2 sa diagnose vpn ike gateway list
show ip dhcp binding execute dhcp lease-list
show access-lists show firewall policy
show ip nat translations diagnose sys session list (after a filter)
show conn (ASA) diagnose sys session list (after a filter)
show processes cpu sorted diagnose sys top 5 20
show logging execute log display (after execute log filter)
show tech-support execute tac report
ping 8.8.8.8 execute ping 8.8.8.8
traceroute 8.8.8.8 execute traceroute 8.8.8.8
monitor capture / capture diagnose sniffer packet <intf> “<bpf>” 4 0 a
debug ip packet diagnose debug flow (see 14.2)
packet-tracer (ASA) diagnose firewall iprope lookup …
undebug all diagnose debug disable + diagnose debug reset
show standby brief diagnose sys vrrp list
show failover (ASA) get system ha status
terminal length 0 config system console / set output standard
18. Closing: What to Do This Week

You will not absorb this by reading it. Nobody ever learned a CLI from a table. Do these five things instead, in this order, on a lab VM or a spare 40F, and the syntax will stop being syntax and start being reflex.

1. Build one VLAN interface, one address object, and one policy with NAT. Get a ping to 8.8.8.8. That single exercise teaches you edit/next/end, the object model, and the policy model in about ten minutes.

2. Break it on purpose. Delete the policy and leave the route. Watch the traffic die while get router info routing-table all still looks perfect. That is the lesson that makes FortiGate click.

3. Run diagnose debug flow on the broken traffic and read the output. When you see iprope_in_check() check failed on policy 0, drop, you will understand why this tool is better than anything on IOS.

4. Build a route-based IPsec tunnel to anything, even another lab FortiGate. Then forget the reverse policy on purpose and watch the tunnel sit up with zero traffic. Now you have made the mistake in a lab instead of in a maintenance window.

5. Type diagnose sys checkused system.interface.name port3 and delete something that is in use. Learning how FortiOS talks about references saves you more time than any other single command.

The FortiGate is not harder than IOS. It is opinionated in different places. It is stricter about objects, looser about interfaces, better at telling you why it dropped your packet, and completely unforgiving about end. Once the grammar lands, most Cisco engineers I have worked with are productive inside a week and prefer the troubleshooting tooling inside a month.

 

 

Recent posts

  • If you've spent any time configuring user authentication on... Full Story

  • DNS is one of those technologies that quietly underpins... Full Story

  • BGP issues on FortiGate firewalls usually trace back to... Full Story

  • Every time your laptop talks to your router, a... Full Story

  • If you've spent any time configuring NAT on a... Full Story

  • If you have spent any time configuring firewall policies... Full Story

  • High availability on FortiGate is one of those features... Full Story

  • If you've configured SD-WAN on a FortiGate, you've almost... Full Story

  • FortiLink is the management protocol that turns a FortiSwitch... Full Story

  • FortiSwitches are pretty rock solid from Mean Time Between... Full Story

  • This is a quicky tip.  Have you ever gone... Full Story

  • DNS is one of those quiet pieces of internet... Full Story

  • This article is an updated version of the previous... Full Story

  • You will add ns2 as a secondary (slave) BIND9... Full Story

  • In the process of deploying my lab, I needed... Full Story

  • RFC 8805, used to be known as Self-Correcting IP... Full Story

  • Years back, I wrote an article about certificate pinning. ... Full Story

  • FortiGates have the ability to send alerts to Microsoft... Full Story

  • In this post, I am going to walk through... Full Story

  • Troubleshooting VoIP on a FortiGate can feel like trying... Full Story

  • Prior to FortiOS 7.0, there were three commands to... Full Story

  • In this post, I am going to go over... Full Story

  • What we are going to do:  We are going... Full Story

  • Choosing between FGCP (FortiGate Clustering Protocol) and FGSP (FortiGate... Full Story

  • Creating a VLAN on macOS (The "Pro" Move) A... Full Story

  • This blog post explores the logic behind how macOS... Full Story

  • Pretty Fly for a Wi-Fi Tell My Wi-Fi Love... Full Story

  • Part of my daily gig is creating BoMs (Bill-of-Materials)... Full Story

  • ICMP introduces several security risks, but careful filtering, rate... Full Story

  • The command diag debug application dhcps -1 enables full... Full Story

  • In the world of FortiOS, execute tac report is... Full Story

  • LLDP; What is it The Link Layer Discovery Protocol... Full Story

  • What it actually does When you run diagnose fdsm... Full Story

  • Monkey Bites are bite-sized, high-impact security insights designed for... Full Story

  • I have run macOS in macOS with Parallels but... Full Story

  • Don't be confused with my other FortiNAC posts where... Full Story

  • This is the third session in a multi-part article... Full Story

  • Today I was configuring key-based authentication on a FortiGate... Full Story

  • Netcat, often called the "Swiss Army knife" of networking,... Full Story

  • At its core, IEEE 802.1X is a network layer... Full Story

  • In case you did not see the previous FortiNAC... Full Story

  • This is our 5th session where we are going... Full Story

  • Now that we have Wireshark installed and somewhat configured,... Full Story

  • The Philosophy of Packet Analysis Troubleshooting isn't about looking... Full Story

  • You have twenty years of muscle memory. You type... Full Story

  • I deployed thousands of ASA firewalls when I was... Full Story

  • Have you ever wanted to do some testing on... Full Story