L2TP/IPsec doesn't work across NAT

I have a L2TP/IPsec server set up. When connecting to it from my Android phone on LTE (important!) the log in my router has this:

respond new phase 1 (Identity Protection): MY-IP[500]<=>PHONE-IP[57803]
ISAKMP-SA established MY-IP[4500]-PHONE-IP[57801] spi:junk-here:junk-here

but there’s nothing else. When connecting to the server on the local network, it works just fine. It seems like I can’t get past phase 1, and I suspect that this has to do with the CGNAT that my phone’s exposed to. The nat-traverse setting in IPsec is already turned ON, so that’s no help. Finally, in IP → IPsec → Active Peers, I see that a connection is made with my phone and the router receives a lot of packets but sends none out.
Is there a fix?
Screenshot from 2021-02-20 16-50-05.png

I wouldn’t think it’s the CGNAT, rather some fragmentation issues. Activate more detailed logging on the Mikrotik and try again:

/system logging add topics=ipsec,!packet
/log print follow-only file=ipsec-start where topics~“ipsec”

then start connection attempt from the phone. Once it fails, break the /log print, download the file and see what it says (or post it after replacing your public IP as suggested in my automatic signature below).

Here’s the debug file. At the beginning there’s a mention of fragmentation, and at the end I found some “authtype mismatch: me:cipher peer:cipher.” Is there any useful info here?
ipsec-start.txt (67.3 KB)

Bump!

The chain of mismatches ends by a match, so it is not relevant. Also the FRAGMENTATION mentioned in the log means that fragmentation of the IKE negotiation packets is supported, i.e. it is not a complaint about existence of fragmented packets.

The log shows that both Phase 1 and Phase 2 have completed successfully:
09:18:40 ipsec IPsec-SA established: ESP/Transport MikRouter[4500]->*Phone[51233] spi=0xd6631ba

Next, the IPsec stack sends keepalive (KA) packets every 20 seconds:

09:18:41 ipsec,debug KA: MikRouter[4500]->*Phone[51233]
09:18:41 ipsec,debug 1 times of 1 bytes message will be sent to *Phone[51233]

09:19:01 ipsec,debug KA: MikRouter[4500]->*Phone[51233]
09:19:01 ipsec,debug 1 times of 1 bytes message will be sent to *Phone[51233]

09:19:21 ipsec,debug KA: MikRouter[4500]->*Phone[51233]
09:19:21 ipsec,debug 1 times of 1 bytes message will be sent to *Phone[51233]

It is normal that no response comes for these, and it has no consequences.

Then, the phone actively terminates the IKE session:
09:19:38 ipsec,debug ===== received 108 bytes from *Phone[51233] to MikRouter[4500]
09:19:38 ipsec,debug receive Information.
09:19:38 ipsec,debug hash(sha2_256)
09:19:38 ipsec,debug hash validated.
09:19:38 ipsec,debug begin.
09:19:38 ipsec,debug seen nptype=8(hash) len=36
09:19:38 ipsec,debug seen nptype=12(delete) len=16
09:19:38 ipsec,debug succeed.
*09:19:38 ipsec,debug Phone delete payload for protocol ESP

So

  • run /system logging add topics=l2tp
  • open two more terminals (cli windows)
  • in one cli window, run /tool sniffer quick port=4500
  • in another cli window, run the /log print follow-only … and repeat the connection attempt
  • 20-40 seconds after starting the connection attempt, run /ip ipsec policy print detail in the third window
  • after the connection attempt fails, stop the /log print … and /tool sniffer quick …
  • run /tool sniffer packet print within a minute after stopping the /tool sniffer quick …
  • download the log file, anonymize it, and post it here together with the output of the /ip ipsec policy print and /tool sniffer packet print

A problem at L2TP level seems unlikely as it works from LAN, but I want to see what’s going on there. What is more likely is that the mobile operator’s firewall closes the pinhole too fast so part of the packets from the Mikrotik do not make it to the phone. And what is also unlikely but possible is that the Mikrotik doesn’t actually send them - that’s why I ask for the /tool sniffer packet print.

The new files are in. Here’s what I got from /ipsec policy print:

 0 T  * group=default src-address=::/0 dst-address=::/0 protocol=all 
        proposal=default template=yes 

 1   DA  peer=l2tp-in-server tunnel=no src-address=***MikRouter***/32 
        src-port=1701 dst-address=***Phone***/32 dst-port=any protocol=udp 
        action=encrypt level=unique ipsec-protocols=esp proposal=default 
        ph2-count=1

ipsec-start-second-try.txt (67.2 KB)
console-dump.txt (3.07 KB)

Show me the complete export of your configuration (see my signature below regarding anonymisation). It seems that a firewall rule is missing in your configuration, as the sniffer shows that the phone is sending encrypted packets between the keepalives but there is no l2tp row in the log.

/interface bridge
add admin-mac=my-address auto-mac=no name=bridge
/interface ethernet
set [ find default-name=ether5 ] poe-out=forced-on
set [ find default-name=sfp1 ] auto-negotiation=no
/interface list
add comment=defconf name=WAN
add comment=defconf name=LAN
/interface wireless security-profiles
set [ find default=yes ] supplicant-identity=MikroTik
/ip ipsec profile
set [ find default=yes ] dpd-interval=disable-dpd hash-algorithm=sha256
/ip ipsec proposal
set [ find default=yes ] auth-algorithms=sha256,sha1 enc-algorithms=aes-256-cbc,aes-192-cbc,aes-128-cbc,blowfish
/ip pool
add name=default-dhcp ranges=192.168.1.10-192.168.1.254
/ip dhcp-server
add add-arp=yes address-pool=default-dhcp disabled=no interface=bridge lease-time=23h59m59s name=defconf
/port
set 0 name=serial0
/ppp profile
set *0 use-encryption=required
/system logging action
set 0 memory-lines=100000
/dude
set data-directory=disk1/dude-data enabled=yes
/interface bridge port
add bridge=bridge comment=defconf interface=ether2
add bridge=bridge comment=defconf interface=ether3
add bridge=bridge comment=defconf interface=ether4
add bridge=bridge comment=defconf interface=ether5
add bridge=bridge interface=ether1
/ip neighbor discovery-settings
set discover-interface-list=LAN
/interface l2tp-server server
set allow-fast-path=yes caller-id-type=number default-profile=default enabled=yes use-ipsec=required
/interface list member
add comment=defconf interface=bridge list=LAN
add comment=defconf interface=sfp1 list=WAN
/interface pptp-server server
set authentication=pap,chap,mschap1,mschap2
/ip address
add address=192.168.1.254/24 comment=defconf interface=bridge network=192.168.1.0
/ip dhcp-client
add comment=defconf disabled=no interface=sfp1 use-peer-dns=no
/ip dhcp-server network
add address=192.168.1.0/24 comment=defconf dns-server=192.168.1.254 gateway=192.168.1.254 netmask=24
/ip dns
set allow-remote-requests=yes cache-size=4096KiB max-concurrent-queries=1000 max-udp-packet-size=8192 servers=192.168.1.59,192.168.1.41
/ip dns static
add address=192.168.1.254 comment=defconf name=router.lan
/ip firewall address-list
add address=192.168.1.59 list=PiHole
/ip firewall filter
add action=accept chain=input comment="defconf: accept established,related,untracked" connection-state=established,related,untracked
add action=accept chain=input in-interface-list=WAN port=500,1703,4500,5500 protocol=udp
add action=accept chain=input in-interface-list=WAN protocol=ipsec-ah
add action=accept chain=input in-interface-list=WAN protocol=ipsec-esp
add action=drop chain=input comment="defconf: drop invalid" connection-state=invalid
add action=accept chain=input comment="defconf: accept ICMP" in-interface-list=LAN protocol=icmp
add action=accept chain=input comment="defconf: accept to local loopback (for CAPsMAN)" dst-address=127.0.0.1
add action=add-src-to-address-list address-list="Blocked IPs" address-list-timeout=6h chain=input comment="add bad IPs to a list" in-interface-list=WAN
add action=drop chain=input comment="defconf: drop all not coming from LAN" in-interface-list=!LAN
add action=accept chain=forward comment="vivint panel" dst-address-list="DoH Servers" src-address=192.168.1.112
add action=accept chain=forward dst-address=192.168.1.112 src-address-list="DoH Servers"
add action=accept chain=forward comment="defconf: accept in ipsec policy" ipsec-policy=in,ipsec
add action=accept chain=forward comment="defconf: accept out ipsec policy" ipsec-policy=out,ipsec
add action=fasttrack-connection chain=forward comment="defconf: fasttrack" connection-state=established,related
add action=accept chain=forward comment="defconf: accept established,related, untracked" connection-state=established,related,untracked
add action=drop chain=forward comment="block DoH" dst-address-list="DoH Servers" src-address-list=!PiHole
add action=reject chain=forward comment="block Quad9 DoH" port=5053 protocol=tcp reject-with=icmp-host-unreachable src-address-list=!PiHole
add action=reject chain=forward comment="block dnscrypt.ca DoH" port=453 protocol=tcp reject-with=icmp-host-unreachable src-address-list=!PiHole
add action=reject chain=forward comment="block DoT" log=yes port=853 protocol=tcp reject-with=icmp-network-unreachable src-address-list=!PiHole
add action=drop chain=forward comment="drop bad IPs going to the NAT" connection-nat-state=dstnat in-interface-list=WAN log=yes src-address-list="Blocked IPs"
add action=drop chain=forward comment="defconf: drop invalid" connection-state=invalid
add action=drop chain=forward comment="defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat connection-state=new in-interface-list=WAN
/ip firewall nat
add action=masquerade chain=srcnat comment="defconf: masquerade" ipsec-policy=out,none out-interface-list=WAN
add action=dst-nat chain=dstnat comment=webservers dst-port=80 in-interface-list=WAN protocol=tcp to-addresses=192.168.1.109 to-ports=80
add action=dst-nat chain=dstnat dst-port=443 in-interface-list=WAN protocol=tcp to-addresses=192.168.1.109 to-ports=443
add action=dst-nat chain=dstnat dst-port=8080 in-interface-list=WAN protocol=tcp to-addresses=192.168.1.64 to-ports=80
add action=dst-nat chain=dstnat comment="SSH access" dst-port=50 in-interface-list=WAN protocol=tcp to-addresses=192.168.1.103 to-ports=50
add action=dst-nat chain=dstnat comment=Minecraft dst-port=25565 in-interface-list=WAN protocol=tcp to-addresses=192.168.1.103 to-ports=25565
add action=dst-nat chain=dstnat disabled=yes dst-port=25566 in-interface-list=WAN protocol=tcp to-addresses=192.168.1.109 to-ports=25566
add action=dst-nat chain=dstnat comment="PiHole DNS redirect; exclude PiHole from redirects" dst-port=53 in-interface-list=LAN protocol=udp src-address-list=!PiHole to-addresses=\
    192.168.1.254 to-ports=53
/ipv6 dhcp-client
add add-default-route=yes interface=sfp1 request=address use-peer-dns=no
/ppp secret
add local-address=192.168.1.80 name=my-name remote-address=192.168.1.1 routes=192.168.1.254 service=l2tp
/snmp
set enabled=yes
/system clock
set time-zone-name=America/New_York
/system identity
set name=MikroTikRouter
/system logging
add topics=ipsec,!packet
add topics=l2tp
/tool graphing interface
add store-on-disk=no
/tool graphing queue
add store-on-disk=no
/tool graphing resource
add store-on-disk=no
/tool mac-server
set allowed-interface-list=LAN
/tool mac-server mac-winbox
set allowed-interface-list=LAN
/tool sniffer
set filter-ip-address=192.168.1.74/32 memory-limit=1000KiB

The port for L2TP is 1701, not 1703.

When you connect via LAN, the rule
action=accept chain=input in-interface-list=WAN port=500,17031701,4500,5500 protocol=udp
is not necessary because the IPsec connection is established via LAN and thus the rule
action=drop chain=forward comment=“defconf: drop all from WAN not DSTNATed” connection-nat-state=!dstnat connection-state=new in-interface-list=WAN
does not drop the packets which need that permissive rule above if they come via WAN.

SOLVED! Switching the port to 1701 now gives me a “Connected” message in my phone. Also, as a side note, that last rule to drop all “WAN not DSTNATed” is listed with 0 packets and 0B in my firewall list. This is weird, considering that I have so many NATted services.

There’s nothing weird about that - the rule counts if it matches and thus drops a packet. Whatever is dstnated does not match the rule, so it doesn’t count.

And I’ve referred to a wrong rule in my previous post - the one which was dropping the L2TP packets coming (IPsec-encrypted) through WAN was the
add action=drop chain=input comment=“defconf: drop all not coming from LAN” in-interface-list=!LAN
one.

Another problem I noticed that is that some firewall rule is blocking me from accessing the router with L2TP, and I know it’s one of those two you mentioned, but I’m not sure how to fix it.

The reason is that once the L2TP client connects, RouterOS dynamically creates an interface called , through which the traffic from the client comes in. This interface is not a member of interface list LAN, so in the absence of any permissive rule, the rule
action=drop chain=input comment=“defconf: drop all not coming from LAN” in-interface-list=!LAN
drops any packet coming in via that interface.

The easiest fix, if you don’t mind that the firewall will treat the L2TP client exactly the same way like LAN clients, is to run
/ppp profile set default interface-list=LAN
After you disconnect and re-connect the client, the interface will be added as a member to interface list LAN.

If you want to use more specific firewall rules for the L2TP clients, you have to create a separate interface list and let the specific permissive rules match on it.